﻿using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.Office.PowerPoint.Y2021.M06.Main;
using ICSharpCode.AvalonEdit.Document;
using IronPython.Runtime;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils;
using LunarSF.SHomeWorkshop.LunarMarkdownEditor.Utils.NPOIHelp;
using Microsoft.Win32;
using NPOI.OpenXmlFormats.Wordprocessing;
using NPOI.XWPF.UserModel;
using NSoup.Helper;
using Org.BouncyCastle.Tls.Crypto.Impl.BC;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Xml.Schema;
using static IronPython.Modules._ast;
using static QRCoder.PayloadGenerator;
using WinForm = System.Windows.Forms;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    /// <summary>
    /// 用以提供类似 Vim 的移动光标模式——Vim 的单键移动太方便了。
    /// </summary>
    public enum EditingMode
    {
        NormalMode,                         //普通模式
        VimMode,                            //类似 Vim 的移动模式
    }

    /// <summary>
    /// 创建者：杨震宇
    /// 创建时间：2013年3月29日
    /// 
    /// 说明：用作文档页面。
    /// </summary>
    public class MarkdownEditor : BetterWpfControls.TabItem
    {
        /// <summary>
        /// 需要添加“加密解锁器”。
        /// </summary>
        private Grid baseGrid = new Grid();

        /// <summary>
        /// 密码设置面板
        /// </summary>
        private SetPasswordPanel setPasswordPanel;

        private InputPasswordPanel inputPasswordPanel;
        /// <summary>
        /// 密码输入面板
        /// </summary>
        public InputPasswordPanel InputPasswordPanel
        {
            get
            {
                return inputPasswordPanel;
            }
        }

        private MarkdownEditorBase editorBase;
        /// <summary>
        /// 主编辑器。
        /// </summary>
        public MarkdownEditorBase EditorBase { get { return this.editorBase; } }

        public MarkdownEditorBase 编辑器内核 { get { return this.editorBase; } }

        private string fullFilePath = null;
        /// <summary>
        /// 文件路径。保存时使用。
        /// </summary>
        public string FullFilePath
        {
            get { return this.fullFilePath; }
            set
            {
                this.fullFilePath = value;

                if (string.IsNullOrEmpty(this.fullFilePath) || File.Exists(this.fullFilePath) == false)
                {
                    this.shortFileName = "unnamed.md";
                    this.headerPanel.ToolTip = null;
                }
                else
                {
                    int lastIndex = this.fullFilePath.LastIndexOf("\\");
                    if (lastIndex >= 0)
                    {
                        string name = this.fullFilePath.Substring(lastIndex + 1);
                        if (string.IsNullOrEmpty(name))
                        {
                            this.shortFileName = "unnamed.md";
                        }
                        else this.shortFileName = name;
                    }
                    else
                    {
                        this.shortFileName = "unnamed.md";
                    }
                }
                RefreshTitle();

                if (ShortFileName.StartsWith("_"))
                {
                    this.icon.Source = WorkspaceTreeViewItem.FolderDocumentImage;
                    var tpTextBlock = new TextBlock() { FontFamily = toolTipFontFamily, FontSize = 14, MaxWidth = 400, TextWrapping = TextWrapping.Wrap, };
                    tpTextBlock.Inlines.Add(new Span(new Run("[目录元文件]")) { FontWeight = FontWeights.Bold, });
                    tpTextBlock.Inlines.Add(new LineBreak());
                    tpTextBlock.Inlines.Add(this.FullFilePath);
                    this.headerPanel.ToolTip = tpTextBlock;
                }
                else
                {
                    this.icon.Source = WorkspaceTreeViewItem.FileBitmapImage;
                    var tpTextBlock = new TextBlock() { FontFamily = toolTipFontFamily, FontSize = 14, MaxWidth = 400, TextWrapping = TextWrapping.Wrap, };
                    tpTextBlock.Inlines.Add(new Span(new Run("[普通MD文件]")) { FontWeight = FontWeights.Bold, });
                    tpTextBlock.Inlines.Add(new LineBreak());
                    tpTextBlock.Inlines.Add(this.FullFilePath);
                    this.headerPanel.ToolTip = tpTextBlock;
                }
            }
        }

        public string 完整文件路径
        {
            get { return FullFilePath; }
            set { FullFilePath = value; }
        }

        public string 文件路径
        {
            get { return FullFilePath; }
            set { FullFilePath = value; }
        }

        private FontFamily toolTipFontFamily = new FontFamily("Arial,SimSun");

        private string title = "";
        /// <summary>
        /// 标签上的文本。如果文档设置了“标题”，应返回标题文本；如果没有，应返回文件短名。
        /// </summary>
        public string Title
        {
            get { return this.title; }
        }

        /// <summary>
        /// 为兼容旧代码而保留。应使用新的“标题文本_非实时”来代替它！
        /// </summary>
        public string 标题文本
        {
            get { return this.title; }
        }

        public string 标题文本_非实时
        {
            get { return this.title; }
        }

        /// <summary>
        /// 没有标题就显示文件短名，有就显示标题。
        /// 标题是指 Markdown 文件中第一个以%开头的行的文本。
        /// </summary>
        private void RefreshTitle()
        {
            var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
            if (string.IsNullOrWhiteSpace(title) == false)
            {
                string subTitle;
                if (title.Length > 20)
                {
                    subTitle = title.Substring(0, 20) + "...";
                }
                else subTitle = title;

                this.headerTextBlock.Text = this.title = subTitle.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
            }
            else
            {
                this.headerTextBlock.Text = this.title = this.shortFileName.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
            }
        }

        private string shortFileName;
        /// <summary>
        /// 取文件路径中最后一个反斜杠之后的部分。如果没有，返回“ex”这两个字符。
        /// </summary>
        public string ShortFileName
        {
            get
            {
                return this.shortFileName;
            }
            set
            {
                this.shortFileName = value;
                var title = MainWindow.GetTitleOfMdFile(this.fullFilePath);
                if (string.IsNullOrWhiteSpace(title) == false)
                {
                    this.headerTextBlock.Text = title.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
                }
                else
                {
                    this.headerTextBlock.Text = this.shortFileName.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
                }
            }
        }

        public string 文件短名
        {
            get
            {
                try
                {
                    var fi = new FileInfo(this.fullFilePath);
                    return fi.Name;
                }
                catch
                {
                    return null;
                }
            }
        }

        private ContextMenu headerContextMenu = new ContextMenu()
        {
            FontFamily = new FontFamily("Arial,SimSun"),
            FontSize = 14,
            Style = Globals.MainWindow.TryFindResource("MetroContextMenu") as Style,
        };

        private ContextMenu editorContextMenu = new ContextMenu()
        {
            FontFamily = new FontFamily("Arial,SimSun"),
            FontSize = 14,
            Style = Globals.MainWindow.TryFindResource("MetroContextMenu") as Style,
        };

        /// <summary>
        /// 直接返回Editor.IsModified的值。
        /// </summary>
        public bool IsModified
        {
            get { return this.editorBase.IsModified; }
        }

        public bool 是否已修改
        {
            get { return this.editorBase.IsModified; }
        }

        #region 编辑器标头菜单项

        /// <summary>
        /// 决定是否将此编辑器“钉”在主选项卡左侧。
        /// </summary>
        private MenuItem miPin;

        private MenuItem miCloseDocument;
        /// <summary>
        /// 关闭文档
        /// </summary>
        internal MenuItem MiCloseDocument { get => miCloseDocument; }

        private MenuItem miCloseOtherDocument;
        /// <summary>
        /// 关闭其他文档
        /// </summary>
        internal MenuItem MiCloseOtherDocument { get => miCloseOtherDocument; }

        private MenuItem miCompileAndPreview;
        /// <summary>
        /// 编译并预览文档
        /// </summary>
        internal MenuItem MiCompileAndPreview { get => miCompileAndPreview; }

        private MenuItem miCallSystemDefaultExplorerOpen;
        /// <summary>
        /// 调用操作系统默认浏览器打开文档（在默认浏览器中预览 Html 网页）
        /// </summary>
        internal MenuItem MiCallSystemDifaultExplorerOpen { get => miCallSystemDefaultExplorerOpen; }

        private MenuItem miSendToLeftCompareArea;
        /// <summary>
        /// 发送到左侧对照区
        /// </summary>
        internal MenuItem MiSendToLeftCompareArea { get => miSendToLeftCompareArea; }

        private MenuItem miSendToRightCompareArea;
        /// <summary>
        /// 发送到右侧对照区
        /// </summary>
        internal MenuItem MiSendToRightCompareArea { get => miSendToRightCompareArea; }

        private MenuItem miSetAsWorkspaceTemplateFile;
        /// <summary>
        /// 设置为当前工作区默认模板
        /// </summary>
        internal MenuItem MiSetAsWorkspaceTemplateFile { get => miSetAsWorkspaceTemplateFile; }

        private MenuItem miShowInExplorer;
        /// <summary>
        /// 在文件资源管理器中定位当前文件
        /// </summary>
        internal MenuItem MiShowInExplorer { get => miShowInExplorer; }

        private MenuItem miCopyFilePath;
        /// <summary>
        /// 复制文档路径
        /// </summary>
        internal MenuItem MiCopyFilePath { get => miCopyFilePath; }

        private MenuItem miCopyFilePathAsPythonString;
        /// <summary>
        /// 复制 Python 字符串格式的文档路径
        /// </summary>
        internal MenuItem MiCopyFilePathAsPythonString { get => miCopyFilePathAsPythonString; }

        private MenuItem miReadOnlySwitcher;
        /// <summary>
        /// 切换当前文档只读状态
        /// </summary>
        internal MenuItem MiReadOnlySwitcher { get => miReadOnlySwitcher; }

        #endregion

        private PresentationWindow presentationWindow = null;
        /// <summary>
        /// 试题演示窗口，考虑到再次打开时应仍然保持试题状态的必要性，每个编辑器都应有一个窗口实例。
        /// </summary>
        public PresentationWindow PresentationWindow
        {
            get
            {
                if (this.presentationWindow == null)
                {
                    this.presentationWindow = new PresentationWindow(this)
                    {
                        Owner = App.Current.MainWindow,
                        WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner,
                        ContentText = this.editorBase.Text,
                    };
                    this.presentationWindow.tbTitle.Text = this.headerTextBlock.Text;
                }
                return this.presentationWindow;
            }
        }

        /// <summary>
        /// [构造方法]创建一个 Markdown 编辑器。
        /// </summary>
        /// <param name="shortFileName">文件短名，用以显示在编辑器的标头上。</param>
        /// <param name="isExamEnabled">是否启用试题编辑功能。（依赖“自动完成”）</param>
        /// <param name="isAutoCompletionEnabled">是否启用“自动完成”功能。</param>
        /// <param name="isEnToChineseDictEnabled">是否启用英译中自动提示与完成功能。（依赖“自动完成”）</param>
        /// <param name="isShowSpaces">是否显示空格。</param>
        /// <param name="isShowEndOfLine">是否显示段落标记。</param>
        /// <param name="isShowTabs">是否显示 Tab 符。</param>
        /// <param name="isCompareAreaEditor">是否对照区。如果是对照区，双击标题头不能预览。</param>
        public MarkdownEditor(string shortFileName, bool isExamEnabled,
            bool isAutoCompletionEnabled, bool isEnToChineseDictEnabled,
            bool isShowSpaces, bool isShowEndOfLine, bool isShowTabs, bool isTextAutoWrap,
            bool isCompareAreaEditor, MarkdownEditorBase.HighlightingType highlightingType)
        {
            this.editorBase = new LunarMarkdownEditor.MarkdownEditorBase(this) { SnapsToDevicePixels = true, };
            RenderOptions.SetEdgeMode(this.editorBase, EdgeMode.Aliased);

            this.isCompareAreaEditor = isCompareAreaEditor;
            this.Style = App.Current.FindResource("BetterTabItemStyle") as Style;
            this.ShortFileName = shortFileName;

            this.IsExamEnabled = isExamEnabled;
            this.IsAutoCompletionEnabled = isAutoCompletionEnabled;
            this.IsEnToChineseDictEnabled = isEnToChineseDictEnabled;
            this.IsShowSpaces = isShowSpaces;
            this.IsShowEndOfLine = isShowEndOfLine;
            this.IsShowTabs = isShowTabs;
            this.IsTextAutoWrap = isTextAutoWrap;

            this.HighlightingSetting = highlightingType;

            #region 初始化标题上下文菜单。
            //ControlTemplate SubMenuItemControlTemplate = Globals.MainWindow.TryFindResource("SubMenuItemControlTemplate") as ControlTemplate;
            //ControlTemplate MidMenuItemControlTemplate = Globals.MainWindow.TryFindResource("MidMenuItemControlTemplate") as ControlTemplate;
            //ControlTemplate MenuItemControlTemplate = Globals.MainWindow.TryFindResource("MenuItemControlTemplate") as ControlTemplate;
            Style mmiStyle = TryFindResource("MetroMenuItem") as Style;

            this.headerPanel.ContextMenu = this.headerContextMenu;

            miPin = new MenuItem()
            {
                Header = "锁定在左侧(_L)",
                Style = mmiStyle,
            };
            miPin.Click += miPin_Click;

            this.headerContextMenu.Items.Add(miPin);

            miCloseDocument = new MenuItem()
            {
                Header = "关闭此文档(_C)",
                InputGestureText = "Ctrl+W",
                Style = mmiStyle,
            };
            miCloseDocument.Click += miCloseDocument_Click;
            this.headerContextMenu.Items.Add(miCloseDocument);

            miCloseOtherDocument = new MenuItem()
            {
                Header = "关闭其它文档(_O)",
                Style = mmiStyle,
            };
            miCloseOtherDocument.Click += MiCloseOtherDocument_Click;
            this.headerContextMenu.Items.Add(miCloseOtherDocument);

            this.headerContextMenu.Items.Add(new Separator());

            miCompileAndPreview = new MenuItem()
            {
                Header = "预览Html文档(_P)",
                ToolTip = "是否先编译受【预览前总是编译】选项影响。",
                InputGestureText = "F5",
                Style = mmiStyle,
            };
            miCompileAndPreview.Click += miCompileAndPreview_Click;
            this.headerContextMenu.Items.Add(miCompileAndPreview);

            miCallSystemDefaultExplorerOpen = new MenuItem()
            {
                Header = "尝试用默认浏览器打开网页(_E)",
                ToolTip = "调用操作系统默认浏览器预览。\r\n是否先编译受【预览前总是编译】选项影响。",
                Style = mmiStyle,
            };

            miCallSystemDefaultExplorerOpen.Click += MiCallSystemDefaultWebBrowserPreview_Click;
            this.headerContextMenu.Items.Add(miCallSystemDefaultExplorerOpen);

            this.headerContextMenu.Items.Add(new Separator());

            miSendToLeftCompareArea = new MenuItem()
            {
                Header = "发送到临时对照区[左](_T)",
                ToolTip = "会将当前选定文本发送过去，可编辑，不可保存。",
                Style = mmiStyle,
            };
            miSendToLeftCompareArea.Click += MiSendToLeftCompareArea_Click;
            this.headerContextMenu.Items.Add(miSendToLeftCompareArea);

            miSendToRightCompareArea = new MenuItem()
            {
                Header = "发送到只读对照区[右](_R)",
                ToolTip = "会将当前所有文本都发送过去，不可编辑，不可保存。",
                Style = mmiStyle,
            };
            miSendToRightCompareArea.Click += MiSendToRightCompareArea_Click;
            this.headerContextMenu.Items.Add(miSendToRightCompareArea);

            this.headerContextMenu.Items.Add(new Separator());

            miSetAsWorkspaceTemplateFile = new MenuItem()
            {
                Header = "设置为当前工作区模板文件(_T)",
                ToolTip = "这样在当前工作区中新建文件时会自动以当前文档的当前内容为准。",
                Style = mmiStyle,
            };
            miSetAsWorkspaceTemplateFile.Click += MiSetAsWorkspaceTemplateFile_Click;
            this.headerContextMenu.Items.Add(miSetAsWorkspaceTemplateFile);

            this.headerContextMenu.Items.Add(new Separator());

            miShowInExplorer = new MenuItem()
            {
                Header = "在资源管理器中定位此文件(_E)",
                ToolTip = "打开 Windows 资源管理器并定位此文件。",
                Style = mmiStyle,
            };
            miShowInExplorer.Click += MiShowInExplorer_Click;
            this.headerContextMenu.Items.Add(miShowInExplorer);

            miCopyFilePath = new MenuItem()
            {
                Header = "复制文件路径【纯字符串】(_F)",
                ToolTip = "复制此 Markdown 文件的完整路径字符串。",
                Style = mmiStyle,
            };
            miCopyFilePath.Click += MiCopyFilePath_Click; ;
            this.headerContextMenu.Items.Add(miCopyFilePath);

            miCopyFilePathAsPythonString = new MenuItem()
            {
                Header = "复制文件路径【Python格式】(_Y)",
                ToolTip = "复制此 Markdown 文件的完整路径为 Python 字符串形式。",
                Style = mmiStyle,
            };
            miCopyFilePathAsPythonString.Click += MiCopyFilePathAsPythonString_Click; ; ;
            this.headerContextMenu.Items.Add(miCopyFilePathAsPythonString);

            this.headerContextMenu.Items.Add(new Separator());

            this.miReadOnlySwitcher = new MenuItem()
            {
                Header = "临时设置为只读文档(_R)",
                ToolTip = "临时设置当前编辑器为只读状态，去除本\r\n菜单项的勾选状态或重新打开文档可恢复。",
                Style = mmiStyle,
            };
            miReadOnlySwitcher.Click += MiReadOnlySwitcher_Click;
            this.headerContextMenu.Items.Add(miReadOnlySwitcher);

            this.headerContextMenu.Opened += HeaderContextMenu_Opened;
            #endregion

            #region 初始化编辑区上下文菜单
            this.editorBase.ContextMenu = this.editorContextMenu;

            MenuItem miUndo = new MenuItem()
            {
                Header = "撤销(_U)",
                InputGestureText = "Ctrl+Z",
                Style = mmiStyle,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_UndoHS.png")),
                },
            };
            miUndo.Click += MiUndo_Click;
            this.editorContextMenu.Items.Add(miUndo);

            MenuItem miRedo = new MenuItem()
            {
                Header = "重做(_R)",
                InputGestureText = "Ctrl+Y",
                Style = mmiStyle,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/Edit_RedoHS.png")),
                },
            };
            miRedo.Click += MiRedo_Click;
            this.editorContextMenu.Items.Add(miRedo);

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miCut = new MenuItem()
            {
                Header = "剪切(_X)",
                InputGestureText = "Ctrl+X",
                Style = mmiStyle,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CutHS.png")),
                },
            };
            miCut.Click += MiCut_Click;
            this.editorContextMenu.Items.Add(miCut);

            MenuItem miCopy = new MenuItem()
            {
                Header = "复制(_C)",
                InputGestureText = "Ctrl+C",
                Style = mmiStyle,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CopyHS.png")),
                },
            };
            miCopy.Click += MiCopy_Click;
            this.editorContextMenu.Items.Add(miCopy);

            MenuItem miPaste = new MenuItem()
            {
                Header = "粘贴(_P)",
                InputGestureText = "Ctrl+V",
                Style = mmiStyle,
                Icon = new Image()
                {
                    Height = 16,
                    Width = 16,
                    SnapsToDevicePixels = true,
                    Source = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/PasteHS.png")),
                },
            };
            miPaste.Click += MiPaste_Click;
            this.editorContextMenu.Items.Add(miPaste);

            if (isCompareAreaEditor == false)
            {
                //对照区是不应该显示这些菜单项的（即使是左对照区允许编辑也不应该）
                this.editorContextMenu.Items.Add(new Separator());

                MenuItem miFindLinkImage = new MenuItem()
                {
                    Header = "预览引用的图像(_P)",
                    InputGestureText = "Ctrl+G",
                    SnapsToDevicePixels = true,
                    Style = mmiStyle,
                };
                miFindLinkImage.Click += MiFindLinkImage_Click;
                this.editorContextMenu.Items.Add(miFindLinkImage);

                MenuItem miOpenLinkedMarkdownFile = new MenuItem()
                {
                    Header = "打开引用的 Markdown 文档(_M)",
                    InputGestureText = "Ctrl+G",
                    SnapsToDevicePixels = true,
                    Style = mmiStyle,
                };
                miOpenLinkedMarkdownFile.Click += MiOpenLinkedMarkdownFile_Click;
                this.editorContextMenu.Items.Add(miOpenLinkedMarkdownFile);

                this.editorContextMenu.Items.Add(new Separator());

                MenuItem miSimpleEncrypt = new MenuItem()
                {
                    Header = "设置密码(_E)",
                    ToolTip = "只支持简单加密，勿用于高要求场合！",
                    SnapsToDevicePixels = true,
                    Style = mmiStyle,
                };
                miSimpleEncrypt.Click += MiSimpleEncrypt_Click;
                this.editorContextMenu.Items.Add(miSimpleEncrypt);

                this.editorContextMenu.Items.Add(new Separator());

                MenuItem miSendToLeftCompareAreaC = new MenuItem()
                {
                    Header = "发送到临时对照区[左](_T)",
                    ToolTip = "仅发送选定文本或所在的树型文字表",
                    Style = mmiStyle,
                };
                miSendToLeftCompareAreaC.Click += MiSendToLeftCompareArea_Click;
                this.editorContextMenu.Items.Add(miSendToLeftCompareAreaC);

                MenuItem miSendToRightCompareAreaC = new MenuItem()
                {
                    Header = "发送到只读对照区[右](_R）",
                    ToolTip = "发送当前文档所有文本",
                    Style = mmiStyle,
                };
                miSendToRightCompareAreaC.Click += MiSendToRightCompareArea_Click;
                this.editorContextMenu.Items.Add(miSendToRightCompareAreaC);
            }

            this.editorContextMenu.Items.Add(new Separator());

            MenuItem miCollapseCurrentFolder = new MenuItem()
            {
                Header = "折叠/展开当前区块(_B)",
                InputGestureText = "Ctrl+Alt+Z",
                SnapsToDevicePixels = true,
                Style = mmiStyle,
            };
            miCollapseCurrentFolder.Click += MiCollapseCurrentFolder_Click;
            this.editorContextMenu.Items.Add(miCollapseCurrentFolder);

            MenuItem miCollapseAllBlocks = new MenuItem()
            {
                Header = "全部折叠(_A)",
                SnapsToDevicePixels = true,
                Style = mmiStyle,
            };
            miCollapseAllBlocks.Click += MiCollapseAllBlocks_Click;
            this.editorContextMenu.Items.Add(miCollapseAllBlocks);

            MenuItem miExpandAllBlocks = new MenuItem()
            {
                Header = "全部展开(_E)",
                SnapsToDevicePixels = true,
                Style = mmiStyle,
            };
            miExpandAllBlocks.Click += MiExpandAllBlocks_Click;
            this.editorContextMenu.Items.Add(miExpandAllBlocks);



            #endregion

            if (isCompareAreaEditor)
            {
                this.closeImage.Visibility = Visibility.Collapsed;
                this.icon.Visibility = Visibility.Collapsed;
            }

            this.iconBorder.Child = icon;
            this.headerPanel.Children.Add(iconBorder);

            Binding modifyStatusBinding = new Binding();
            modifyStatusBinding.Source = this.editorBase;
            modifyStatusBinding.Path = new PropertyPath("IsModified");
            modifyStatusBinding.Converter = new LunarMarkdownEditor.Converter.ModifiedToStar();
            modifyStatusBinding.Mode = BindingMode.OneWay;
            BindingOperations.SetBinding(modifiedTextBlock, TextBlock.TextProperty, modifyStatusBinding);

            this.readonlyTextBlock.MouseLeftButtonDown += ReadonlyTextBlock_MouseLeftButtonDown;

            this.headerPanel.Children.Add(this.modifiedTextBlock);
            this.headerPanel.Children.Add(this.readonlyTextBlock);
            this.headerPanel.Children.Add(this.headerTextBlock);
            this.closeImage.Source = closeImageSource;
            this.closeImage.MouseLeftButtonDown += closeImage_MouseLeftButtonDown;
            this.closeBorder.Child = this.closeImage;
            this.headerPanel.Children.Add(this.closeBorder);
            this.Header = this.headerPanel;

            //这个方案有问题
            //this.editorBase.Document.UndoStack.PropertyChanged += UndoStack_PropertyChanged;

            this.headerTextBlock.MouseLeftButtonDown += HeaderTextBlock_MouseLeftButtonDown;

            //处理拖动
            this.AllowDrop = true;
            this.Drop += MarkdownEditor_Drop;

            this.closeBorder.MouseLeave += closeBorder_MouseLeave;
            this.closeBorder.MouseEnter += closeBorder_MouseEnter;

            this.PreviewKeyDown += MarkdownEditor_PreviewKeyDown;
            this.PreviewTextInput += MarkdownEditor_PreviewTextInput;
            this.PreviewKeyUp += MarkdownEditor_PreviewKeyUp;

            this.editorBase.WordWrap = Globals.MainWindow.TextAutoWrap;

            //处理加密面板布局
            this.setPasswordPanel = new SetPasswordPanel(this);
            this.inputPasswordPanel = new InputPasswordPanel(this);

            this.Content = this.baseGrid;
            this.baseGrid.Children.Add(this.editorBase);
            this.baseGrid.Children.Add(this.setPasswordPanel);
            this.baseGrid.Children.Add(this.InputPasswordPanel);

            //如果文档未加密，直接显示
            if (IsEncrypted(this.fullFilePath) == false)
            {
                this.setPasswordPanel.Visibility =
                this.InputPasswordPanel.Visibility = Visibility.Collapsed;
            }
            else
            {
                this.InputPasswordPanel.Visibility = Visibility.Visible;
                this.setPasswordPanel.Visibility = Visibility.Collapsed;
            }

            //双击预览图像
            this.editorBase.TextArea.PreviewMouseDoubleClick += TextArea_PreviewMouseDoubleClick;

            if (this.editorBase.IsReadOnly)
            {
                this.readonlyTextBlock.Visibility = Visibility.Visible;
            }
            else
            {
                this.readonlyTextBlock.Visibility = Visibility.Collapsed;
            }
        }

        private void ReadonlyTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            MiReadOnlySwitcher_Click(sender, e);
        }

        private void MiReadOnlySwitcher_Click(object sender, RoutedEventArgs e)
        {
            this.editorBase.IsReadOnly = !this.editorBase.IsReadOnly;

            miReadOnlySwitcher.IsChecked = this.editorBase.IsReadOnly;

            if (this.editorBase.IsReadOnly)
            {
                this.readonlyTextBlock.Visibility = Visibility.Visible;
            }
            else
            {
                this.readonlyTextBlock.Visibility = Visibility.Collapsed;
            }
        }

        private void MiCopyFilePathAsPythonString_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                System.Windows.Clipboard.SetDataObject("r\'" + this.FullFilePath.Replace('\\', '/') + "\'");
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void MiCopyFilePath_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                System.Windows.Clipboard.SetDataObject(this.FullFilePath);
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void MiShowInExplorer_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                System.Diagnostics.Process.Start("explorer.exe", "/select, " +
                    (this.FullFilePath.Contains(' ') ? ("\"" + this.FullFilePath + "\"") : this.FullFilePath));
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        private void MiSetAsWorkspaceTemplateFile_Click(object sender, RoutedEventArgs e)
        {
            var ae = Globals.MainWindow.ActivedEditor;
            if (ae == null)
            {
                LMessageBox.Show("当前没有打开文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            MessageBoxResult result;
            var title = ae.Title;
            if (string.IsNullOrWhiteSpace(title)) title = ae.ShortFileName;

            if (File.Exists(Globals.PathOfWorkspaceCommonTemplateFile))
            {
                result = LMessageBox.Show($"当前工作区已存在用来新建文档的模板，要用当前活动文档【{ae.Title}】覆盖吗？",
                    Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Warning);
            }
            else
            {
                result = LMessageBox.Show($"要用当前活动文档【{ae.Title}】的内容作为当前工作区新建文档的模板吗？",
                    Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Warning);
            }

            if (result != MessageBoxResult.OK) return;

            try
            {
                var srcText = ae.EditorBase.Text;
                StringBuilder sb = new StringBuilder();

                //要去除标题和第一行时间注释
                var lines = srcText.Split(new string[] { "\r\n" }, StringSplitOptions.None);

                if (lines.Length <= 0)
                {
                    LMessageBox.Show("当前文档没有实际内容，不能作为模板。",
                        Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Warning);
                }

                if (CustomMarkdownSupport.IsTaskLine(lines[0]) == false) sb.Append(lines[0]);//如果第行是文档完成情况，直接忽略。

                bool gotTitle = false;
                bool gotCreateTime = false;

                //文档创建时间类似：
                //     ；2017/1/4 14:35:57
                Regex regCreateTime = new Regex(@"^；\d\d\d\d\/\d{1,2}\/\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}");

                for (int i = 1; i < lines.Length; i++)
                {
                    var s = lines[i];
                    if (gotTitle == false && (s.StartsWith("%") || s.StartsWith("％")))
                    {
                        gotTitle = true;
                        continue;//如果碰到第一个标题文本，直接忽略
                    }

                    if (gotCreateTime == false && regCreateTime.Match(s).Success)
                    {
                        sb.Clear();//如果存在第一个创建时间注释，就以此注释后一行为起点。
                        gotCreateTime = true;
                        continue;
                    }

                    sb.Append(s);
                    sb.Append("\r\n");
                }

                using (StreamWriter sw = File.CreateText(Globals.PathOfWorkspaceCommonTemplateFile))
                {
                    sw.Write(sb.ToString());
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        #region 提供类似 Vim 的单键选取模式

        private EditingMode editingMode = EditingMode.NormalMode;

        public EditingMode EditingMode
        {
            get { return this.editingMode; }
            set
            {
                this.editingMode = value;
                if (value == EditingMode.VimMode)
                {
                    this.EditorBase.EditingMode = ICSharpCode.AvalonEdit.EditingMode.VimMode;
                    this.editorBase.TextArea.Caret.CaretBrush = Brushes.Red;
                    this.editorBase.TextArea.MovingMode = true;
                    Globals.SwitchInputMethod(false);//自动切换输入法状态。

                    Globals.MainWindow.bdInputedCommandText.Visibility = Visibility.Visible;
                }
                else
                {
                    this.EditorBase.EditingMode = ICSharpCode.AvalonEdit.EditingMode.NormalMode;
                    this.editorBase.TextArea.MovingMode = false;
                    this.editorBase.TextArea.Caret.CaretBrush = (Brush)this.editorBase.TextArea.TextView.GetValue(TextBlock.ForegroundProperty);

                    Globals.MainWindow.bdInputedCommandText.Visibility = Visibility.Collapsed;
                }
            }
        }

        private int vimKeyUpedCount = 0;

        private DateTime? previewVimKeyPressedTime = null;

        private InputMethodState? previewInputMethodState = null;

        public InputMethodState? PreviewInputMethodState
        {
            get { return previewInputMethodState; }
            set { previewInputMethodState = value; }
        }



        private void MarkdownEditor_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            //半秒内连续按两次 vimKey。
            var vimKey = Globals.VimKey;
            if (e.Key != vimKey || vimKey == Key.None)  //禁用的情况。
            {
                previewVimKeyPressedTime = null;
                vimKeyUpedCount = 0;
                e.Handled = true;
                return;
            }

            if (previewVimKeyPressedTime == null)
            {
                vimKeyUpedCount += 1;
                previewVimKeyPressedTime = DateTime.Now;
                e.Handled = true;
                return;
            }

            if (vimKeyUpedCount > 0 && previewVimKeyPressedTime.HasValue)
            {
                var timeSpan = DateTime.Now - previewVimKeyPressedTime;
                if (timeSpan.Value.Milliseconds <= 500)
                {
                    previewVimKeyPressedTime = null;
                    vimKeyUpedCount = 0;
                    e.Handled = true;

                    //半秒之内连续按两次 LeftShift 键，即开启“移动模式（MovingMode）”
                    if (InputMethod.Current != null)
                    {
                        this.previewInputMethodState = InputMethod.Current.ImeState;
                    }

                    this.EditingMode = EditingMode.VimMode;

                    //vim为防止出错，默认行为就是：返回命令模式时向前移动插入点（只有在行首是例外）。
                    var selStartOffset = editorBase.SelectionStart;
                    if (editorBase.SelectionLength > 0)
                    {
                        editorBase.Select(selStartOffset, 0);
                    }

                    var lineOffset = editorBase.Document.GetLineByOffset(selStartOffset).Offset;
                    if (selStartOffset > lineOffset)
                    {
                        editorBase.MoveCaret(ICSharpCode.AvalonEdit.Editing.CaretMovementType.CharLeft);
                    }
                    return;
                }
                else
                {
                    vimKeyUpedCount += 1;
                    previewVimKeyPressedTime = DateTime.Now;
                    e.Handled = true;
                    return;
                }
            }
        }

        #endregion 提供类似 Vim 的单键选取模式

        private void TextArea_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding && Globals.MainWindow.PerspectiveMode == Perspective.EditingAndPresentation)
            {
                //在“演讲者模式”下，双击被折叠起来的图像链接、二维文字表、树型列表
                foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
                {
                    if (i.IsFolded == false) continue;

                    if (this.editorBase.SelectionStart >= i.StartOffset && this.editorBase.SelectionStart <= i.EndOffset)
                    {
                        if (i.Title.StartsWith("[") && i.Title.EndsWith("]()"))
                        {
                            //TODO：暂时不支持文件链接的预览。
                            return;
                        }

                        if (i.Title.StartsWith("![") && i.Title.EndsWith("]()"))
                        {
                            PreviewLinkedImageFile(false, i.TextContent);
                            e.Handled = true;
                            return;
                        }

                        if ((i.Title.StartsWith("┣") && i.Title.EndsWith("┫")) ||
                            (i.Title.StartsWith("{") && i.Title.EndsWith("}")) ||
                            /*(i.Title.StartsWith("> ") || i.Title.StartsWith("引用块：")) ||*/
                            //实践证明引用块也支持这样的折叠效果不好——有画蛇添足的感觉。
                            CustomMarkdownSupport.IsTreeListTextLine(i.Title))
                        {
                            var tableText = i.TextContent;
                            CompileAndPresentateHtml(
                                CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment,
                                false, tableText);

                            //如果当前正在演示状态，就不展开折叠区了。
                            //如果在编辑状态，双击不但预览图像文件，而且展开折叠区。
                            //if (Globals.MainWindow.rdMainMenuArea.ActualHeight <= 0)
                            e.Handled = true;
                            return;
                        }
                    }
                }
            }

            //二维文字表在展开时不支持双击预览！但单独占一行的图像文件链接则支持。
            //这是因为二维文字表双击往往是用以编辑而非预览。
            //图像链接的预览基本不影响编辑操作；
            //但二维文字表的预览是通过弹出新窗口（并编译为Html）来实现的，如果支持“双击左键预览”会打断用户编辑。
            Globals.MainWindow.PreviewImage();
        }

        private void MiCloseOtherDocument_Click(object sender, RoutedEventArgs e)
        {
            for (int i = Globals.MainWindow.mainTabControl.Items.Count - 1; i >= 0; i--)
            {
                var editor = Globals.MainWindow.mainTabControl.Items[i] as MarkdownEditor;
                if (editor == null || editor == this) continue;

                editor.Close();
            }
        }

        /// <summary>
        /// 这个 Border 包围在图标外侧，它的底部边框色彩用于表示当前文档的编辑状态。
        /// </summary>
        private Border iconBorder = new Border()
        {
            BorderBrush = Brushes.Red,
            BorderThickness = new Thickness(0, 0, 0, 4),
        };

        /// <summary>
        /// 表示是否元文件的图标。
        /// </summary>
        private Image icon = new Image() { Width = 16, Margin = new Thickness(0, 2, 2, 2), };

        /// <summary>
        /// 取当前编辑器使用的图标。
        /// </summary>
        public ImageSource IconSource
        {
            get { return icon.Source; }
        }

        /// <summary>
        /// 展开所有折叠块。
        /// </summary>
        private void MiExpandAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = false;
            }
        }

        /// <summary>
        /// 折叠所有可折叠的区域。
        /// </summary>
        private void MiCollapseAllBlocks_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            foreach (var i in this.EditorBase.FoldingManager.AllFoldings)
            {
                i.IsFolded = true;
            }
        }

        /// <summary>
        /// 折叠当前所在的可折叠区域。
        /// </summary>
        private void MiCollapseCurrentFolder_Click(object sender, RoutedEventArgs e)
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            this.EditorBase.FoldSelectedBlock();
        }

        /// <summary>
        /// 发送到右对照区。LME 不支持同一文档的多视图编辑，故需要对照时借用了右工具栏。
        /// 右对照区不可编辑，且不会保持状态，程序关闭就消失。
        /// </summary>
        private void MiSendToRightCompareArea_Click(object sender, RoutedEventArgs e)
        {
            SendToRightCompareArea();
        }

        /// <summary>
        /// 发送到右对照区。
        /// </summary>
        public void SendToRightCompareArea()
        {
            if (this.inputPasswordPanel.Visibility != Visibility.Collapsed)
            {
                LMessageBox.Show("请先输入密码打开文档！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            MarkdownEditor compareEdit = null;
            foreach (UIElement ue in Globals.MainWindow.tcRightToolBar.Items)
            {
                var edit = ue as MarkdownEditor;
                if (edit == null) continue;
                if (edit.ShortFileName == "对照区[只读]")
                {
                    compareEdit = edit;
                    break;
                }
            }

            if (compareEdit == null)
            {
                compareEdit = new MarkdownEditor("对照区[只读]", false, false, false,
                    Globals.MainWindow.IsShowTabs, Globals.MainWindow.IsShowEndOfLine,
                    Globals.MainWindow.IsShowTabs, Globals.MainWindow.TextAutoWrap, true, this.HighlightingSetting);
                compareEdit.headerPanel.ContextMenu = null;
                // 注意：由于之前的设计，这些操作是全局性的，所以会自动识别主编辑区并调用。故这里保留菜单项也没用。
                compareEdit.editorContextMenu.Visibility = Visibility.Collapsed;
                foreach (UIElement ue in compareEdit.editorContextMenu.Items)
                {
                    MenuItem mi = ue as MenuItem;
                    if (mi == null)
                    {
                        var s = ue as Separator;
                        if (s != null) s.Visibility = Visibility.Collapsed;
                        continue;
                    }

                    mi.IsEnabled = false;
                    mi.Visibility = Visibility.Collapsed;
                }
                compareEdit.EditorBase.IsReadOnly = true;
                compareEdit.EditorBase.FontSize = Globals.MainWindow.mainTabControl.FontSize;
                compareEdit.editorBase.FontFamily = Globals.MainWindow.mainTabControl.FontFamily;
                Globals.MainWindow.tcRightToolBar.Items.Add(compareEdit);
            }

            compareEdit.EditorBase.WordWrap = this.EditorBase.WordWrap;
            compareEdit.EditorBase.Document.Text = this.EditorBase.Text;
            compareEdit.EditorBase.IsModified = false;
            compareEdit.EditorBase.IsShowEndOfLine = this.EditorBase.IsShowEndOfLine;
            compareEdit.EditorBase.IsShowSpaces = this.EditorBase.IsShowSpaces;
            compareEdit.EditorBase.IsShowTabs = this.EditorBase.IsShowTabs;
            Globals.MainWindow.tcRightToolBar.SelectedItem = compareEdit;

            if (Globals.MainWindow.tcRightToolBar.ActualWidth < 100)
            {
                Globals.MainWindow.cdMainEditArea.Width =
                    Globals.MainWindow.cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
            }
        }

        public void 发送到右侧对照区()
        {
            SendToRightCompareArea();
        }

        /// <summary>
        /// 发送到左对照区。LME 不支持同一文档的多视图编辑，故需要对照时借用了工具栏。
        /// 左对照区可编辑，但也不会保持状态，程序关闭就消失。
        /// 左对照区只会将选定文本发送到对照区，通常发送树型文字表比较好。
        /// </summary>
        private void MiSendToLeftCompareArea_Click(object sender, RoutedEventArgs e)
        {
            SendToLeftCompareArea();
        }

        public void SendToLeftCompareArea()
        {
            if (this.inputPasswordPanel.Visibility != Visibility.Collapsed)
            {
                LMessageBox.Show("请先输入密码打开文档！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            MarkdownEditor compareEdit = null;
            foreach (UIElement ue in Globals.MainWindow.tcManagerPanels.Items)
            {
                var edit = ue as MarkdownEditor;
                if (edit == null) continue;
                if (edit.ShortFileName == "对照区[临时]")
                {
                    compareEdit = edit;
                    break;
                }
            }

            if (compareEdit == null)
            {
                compareEdit = new MarkdownEditor("对照区[临时]", false, false, false,
                    Globals.MainWindow.IsShowTabs, Globals.MainWindow.IsShowEndOfLine,
                    Globals.MainWindow.IsShowTabs, Globals.MainWindow.TextAutoWrap, true, this.HighlightingSetting);
                compareEdit.headerPanel.ContextMenu = null;
                compareEdit.editorContextMenu.Visibility = Visibility.Collapsed;
                #region 左对照区可编辑、不可保存
                // 注意：由于之前的设计，这些操作是全局性的，所以会自动识别主编辑区并调用。故这里保留菜单项也没用。
                foreach (UIElement ue in compareEdit.editorContextMenu.Items)
                {
                    MenuItem mi = ue as MenuItem;
                    if (mi == null)
                    {
                        var s = ue as Separator;
                        if (s != null) s.Visibility = Visibility.Collapsed;
                        continue;
                    }

                    mi.IsEnabled = false;
                    mi.Visibility = Visibility.Collapsed;
                }
                #endregion

                compareEdit.EditorBase.IsReadOnly = false;
                compareEdit.EditorBase.FontSize = Globals.MainWindow.mainTabControl.FontSize;
                compareEdit.editorBase.FontFamily = Globals.MainWindow.mainTabControl.FontFamily;
                Globals.MainWindow.tcManagerPanels.Items.Add(compareEdit);
            }

            string willSendText;
            if (this.editorBase.SelectionLength == 0)
            {
                var curLine = this.EditorBase.Document.GetLineByOffset(this.EditorBase.SelectionStart);
                var curLineText = this.EditorBase.Document.GetText(curLine.Offset, curLine.Length);

                string header, tail; int level;
                if (MarkdownEditorBase.IsTreeListLine(curLineText, out header, out tail, out level) &&
                    CustomMarkdownSupport.IsImageLinkLine(curLineText) == false)
                {
                    List<string> preLines = new List<string>();
                    var preLine = curLine.PreviousLine;
                    while (preLine != null)
                    {
                        var preLineText = this.EditorBase.Document.GetText(preLine.Offset, preLine.Length);
                        string preHeader, preTail; int preLevel;
                        if (MarkdownEditorBase.IsTreeListLine(preLineText, out preHeader, out preTail, out preLevel) &&
                            CustomMarkdownSupport.IsImageLinkLine(preLineText) == false)
                        {
                            preLines.Add(preLineText);
                            preLine = preLine.PreviousLine;
                            continue;
                        }
                        else break;
                    }

                    List<string> nextLines = new List<string>();
                    var nextLine = curLine.NextLine;
                    while (nextLine != null)
                    {
                        var nextLineText = this.EditorBase.Document.GetText(nextLine.Offset, nextLine.Length);
                        string nextHeader, nextTail; int nextLevel;
                        if (MarkdownEditorBase.IsTreeListLine(nextLineText, out nextHeader, out nextTail, out nextLevel) &&
                            CustomMarkdownSupport.IsImageLinkLine(nextLineText) == false)
                        {
                            nextLines.Add(nextLineText);
                            nextLine = nextLine.NextLine;
                            continue;
                        }
                        else break;
                    }

                    StringBuilder sb = new StringBuilder();
                    for (int i = preLines.Count - 1; i >= 0; i--)
                    {
                        sb.Append(preLines[i]);
                        sb.Append("\r\n");
                    }

                    for (int i = 0; i < nextLines.Count; i++)
                    {
                        sb.Append(nextLines[i]);
                        sb.Append("\r\n");
                    }

                    willSendText = sb.ToString();
                }
                else
                {
                    var result = LMessageBox.Show("没有选择文本，而且插入点所在位置也不是个树型文字表！\r\n　　-------------------------------------------\r\n　　要尝试将所有标题都当作提纲发送到临时对照区[左]吗？", Globals.AppName,
                          MessageBoxButton.YesNo, MessageBoxImage.Warning);
                    if (result == MessageBoxResult.Yes)
                    {
                        StringBuilder sb = new StringBuilder();

                        var line = this.editorBase.Document.GetLineByNumber(1);
                        while (line != null)
                        {
                            var lineText = this.editorBase.Document.GetText(line.Offset, line.Length);
                            if (lineText.StartsWith("#"))
                            {
                                sb.Append(lineText);
                                sb.Append("\r\n");
                            }
                            line = line.NextLine;
                        }
                        willSendText = sb.ToString();
                    }
                    else return;
                }
            }
            else willSendText = this.editorBase.SelectedText;

            compareEdit.EditorBase.WordWrap = this.EditorBase.WordWrap;
            compareEdit.EditorBase.Document.Text = willSendText;
            compareEdit.editorBase.IsModified = false;
            compareEdit.EditorBase.IsShowEndOfLine = this.EditorBase.IsShowEndOfLine;
            compareEdit.EditorBase.IsShowSpaces = this.EditorBase.IsShowSpaces;
            compareEdit.EditorBase.IsShowTabs = this.EditorBase.IsShowTabs;
            Globals.MainWindow.tcManagerPanels.SelectedItem = compareEdit;

            if (Globals.MainWindow.leftToolBarGrid.Visibility != System.Windows.Visibility.Visible ||
                            Globals.MainWindow.leftToolBarGrid.ActualWidth < 100)
            {
                Globals.MainWindow.cdLeftToolsArea.Width = new GridLength(460, GridUnitType.Pixel);
            }
        }

        public void 发送到左侧对照区()
        {
            SendToLeftCompareArea();
        }

        private void HeaderContextMenu_Opened(object sender, RoutedEventArgs e)
        {
            miPin.IsChecked = BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this);
        }

        /// <summary>
        /// 跳转到引用的 Markdown 文档。
        /// </summary>
        private void MiOpenLinkedMarkdownFile_Click(object sender, RoutedEventArgs e)
        {
            if (GotoAnchorInSameDocument()) return;

            OpenLinkedMarkdownFile();
        }

        /// <summary>
        /// 如果是页面内的锚，直接跳转即可。
        /// </summary>
        public bool GotoAnchorInSameDocument()
        {
            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            Regex regexAnchorRef = new Regex(@"\[.*\]\(\#.{1,}\)");
            MatchCollection matchesOfAnchor = regexAnchorRef.Matches(lineText);
            if (matchesOfAnchor.Count > 0)
            {
                var selOffsetInLine = this.editorBase.SelectionStart - line.Offset;
                foreach (Match match in matchesOfAnchor)
                {
                    if (selOffsetInLine >= match.Index && selOffsetInLine < match.Index + match.Length)
                    {
                        Regex regexAnchorRef2 = new Regex(@"(?<=(\[.*\]\(\#)).{1,}(?=(\)))");
                        var match2 = regexAnchorRef2.Match(match.Value);
                        if (match2.Success)
                        {
                            Regex regexAnchor = new Regex(@"(?<=(\[.*\]\(@))" + match2.Value + @"(?=(\)))");
                            var matchAnchor = regexAnchor.Match(this.editorBase.Document.Text);
                            if (matchAnchor.Success)
                            {
                                var destLine = this.editorBase.Document.GetLineByOffset(matchAnchor.Index);
                                this.editorBase.Select(matchAnchor.Index, matchAnchor.Length);
                                this.editorBase.ScrollTo(destLine.LineNumber, matchAnchor.Index - destLine.Offset);
                                return true;
                            }
                        }

                        return false;
                    }
                }

                return false;
            }

            return false;
        }

        /// <summary>
        /// 跳转到引用的 Markdown 文档。注意：此方法不支持跳转到同一个文档中引用的“锚”。
        /// </summary>
        internal void OpenLinkedMarkdownFile()
        {
            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            var selIndex = this.editorBase.SelectionStart - line.Offset;

            int leftBracketIndex;
            int rightBracketIndex;
            if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
            {
                leftBracketIndex = lineText.IndexOf("](", selIndex);
                rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                if (rightBracketIndex < 0)
                    rightBracketIndex = lineText.IndexOf(")", selIndex);
            }
            else
            {
                leftBracketIndex = lineText.LastIndexOf("](", selIndex);

                if (leftBracketIndex < 0)
                {
                    if (selIndex >= 1)
                    {
                        leftBracketIndex = lineText.LastIndexOf("[", selIndex - 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                    }
                    else
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", selIndex);
                    }
                }
                else
                {
                    rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                    if (rightBracketIndex < 0)
                        rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                }
            }

            if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (File.Exists(this.FullFilePath) == false)
            {
                LMessageBox.Show("正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', '　', '\t' }); ;

            var index = relativePath.ToLower().LastIndexOf(".html");

            if (index < 0)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                    MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var anchor = relativePath.Substring(index + 5);
            relativePath = relativePath.Substring(0, index) + ".md";//这两行顺序不能倒。

            if (anchor.StartsWith("#"))
            {
                anchor = $"](@{anchor.Substring(1)})";
            }
            else anchor = "";


            var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对 Markdown 文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
            var directory = thisFileInfo.Directory;

            int x = 0;
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                    x++;
                }
                else
                {
                    var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);

                    if (Directory.Exists(subPath) == false)
                    {
                        var subPathInfo = new FileInfo(subPath);

                        //用 ~ 代替 _ 风险太高了！
                        //if(subPathInfo.Name.StartsWith("~") && subPathInfo.Extension == ".md")
                        //{
                        //    subPath = subPathInfo.Directory.FullName + "\\_" + subPathInfo.Name.Substring(1);
                        //}

                        if (File.Exists(subPath))
                        {
                            //就是目标
                            Globals.MainWindow.OpenDocuments(new string[] { subPath });

                            MarkdownEditor newEditor = null;
                            var newIndexOfDocument = -1;
                            for (int i = 0; i < Globals.MainWindow.mainTabControl.Items.Count; i++)
                            {
                                var editor = Globals.MainWindow.mainTabControl.Items[i] as MarkdownEditor;
                                if (editor != null && editor.FullFilePath.ToLower() == subPath.ToLower())
                                {
                                    newEditor = editor;
                                    newIndexOfDocument = i;
                                    break;
                                }
                            }

                            if (newEditor != null)
                            {
                                Globals.MainWindow.mainTabControl.SelectedIndex = newIndexOfDocument;
                                if (string.IsNullOrEmpty(anchor) == false && anchor.Length > 4)
                                {
                                    var startIndex = newEditor.EditorBase.Text.IndexOf(anchor);
                                    if (startIndex >= 0 && (startIndex + 3) < newEditor.editorBase.Document.TextLength)
                                    {
                                        newEditor.EditorBase.Select(startIndex + 3, anchor.Length - 4);
                                        var destLine = newEditor.EditorBase.Document.GetLineByOffset(startIndex + 3);
                                        newEditor.EditorBase.ScrollTo(destLine.LineNumber, startIndex + 3 - destLine.Offset);
                                    }
                                }
                            }
                            return;
                        }

                        LMessageBox.Show("当前插入点位置处的文本不像是个对 Markdown 文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    else
                    {
                        directory = new DirectoryInfo(subPath);
                        x++;
                    }
                }
            }

            if (x != pieces.Length - 1)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对 Markdown 文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
        }

        /// <summary>
        /// 对当前文档进行简单地加密。
        /// </summary>
        private void MiSimpleEncrypt_Click(object sender, RoutedEventArgs e)
        {
            if (File.Exists(this.FullFilePath) == false || this.IsModified)
            {
                LMessageBox.Show("此功能需要先保存文件！", Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (string.IsNullOrEmpty(this.Password) == false)
            {
                var result = InputBox.Show(Globals.AppName, $"　　请输入现有密码以便设置新密码！\r\n\r\n提示：{this.PasswordTip}", "", false, null, true);
                if (string.IsNullOrEmpty(result) || result != this.Password)
                {
                    LMessageBox.Show("您输入的密码不正确！无法设置新密码！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                    return;
                }
            }

            setPasswordPanel.Visibility = Visibility.Visible;
            if (InputPasswordPanel.Visibility != Visibility.Collapsed)
            {
                InputPasswordPanel.Visibility = Visibility.Collapsed;
            }
        }

        private string passwordTip = "";
        /// <summary>
        /// 密码提示文本。
        /// </summary>
        public string PasswordTip
        {
            get { return this.passwordTip; }
            set
            {
                this.passwordTip =
                this.InputPasswordPanel.tipBlock.Text = value;
            }
        }

        /// <summary>
        /// 密码文本。
        /// </summary>
        public string Password { get; set; } = "";

        /// <summary>
        /// 首行文本，保存密码、提示问题等信息。
        /// </summary>
        public string EncryptedFirstLine
        {
            get
            {
                if (string.IsNullOrEmpty(this.Password) && string.IsNullOrEmpty(this.PasswordTip))
                {
                    return "";//没有密码。
                }

                var fstLine = new String(SetPasswordPanel.TextEncrypt($"Question:{PasswordTip}|Password:{Password}", "DyBj#PpBb")).Replace("\r", "[[<r>]]").Replace("\n", "[[<n>]]");

                return fstLine;
            }
        }

        /// <summary>
        /// 根据路径判断该文件是否已被加密。
        /// </summary>
        /// <param name="fullFilePath">要检查的 Markdown 文件的完整路径。</param>
        public static bool IsEncrypted(string fullFilePath)
        {
            if (String.IsNullOrWhiteSpace(fullFilePath) || File.Exists(fullFilePath) == false) return false;

            System.IO.StreamReader s = File.OpenText(fullFilePath);
            var fstEncriptLine = s.ReadLine();
            if (string.IsNullOrEmpty(fstEncriptLine)) return false;

            var fstDecriptedLine = SetPasswordPanel.TextDecrypt(fstEncriptLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
            if (fstDecriptedLine.Contains("Password:") && fstDecriptedLine.Contains("Question:") && fstDecriptedLine.Contains("|"))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 单词拼写错误，为向前兼容才保留的。
        /// </summary>
        public static bool IsEncripied(string fullFilePath)
        {
            return IsEncrypted(fullFilePath);
        }

        public static bool 是否被加密(string fullFilePath)
        {
            return IsEncrypted(fullFilePath);
        }

        /// <summary>
        /// 预览引用的图像文件。
        /// </summary>
        private void MiFindLinkImage_Click(object sender, RoutedEventArgs e)
        {
            PreviewLinkedImageFile();
        }

        public bool PreviewQuickLinkedImage(bool showChildWindow)
        {
            var selStart = this.EditorBase.SelectionStart;
            var line = this.EditorBase.Document.GetLineByOffset(selStart);
            var lineText = this.EditorBase.Document.GetText(line.Offset, line.Length);
            var pieces = Utils.Text.SplitToPiecesByRegex(@"[\(（][:：][a-zA-Z0-9\-_\.\\/]{2,}[:：][\)）]", lineText);
            var selIndexInLine = selStart - line.Offset;
            foreach (var piece in pieces)
            {
                if (piece.IsMartchText)
                {
                    if (selIndexInLine >= piece.StartIndex && selIndexInLine < piece.StartIndex + piece.Length)
                    {
                        var shortLink = piece.SourceText.Replace("/", "\\").Trim(new char[] { ' ', '　', '\t', '(', ')', '（', '）', ':', '：', '\\', });
                        if (shortLink.EndsWith("."))
                            shortLink += "png";
                        var longLink = Globals.PathOfWorkspace + "Images~\\" + shortLink;
                        if (File.Exists(longLink))
                        {
                            if (WorkspaceTreeViewItem.IsValidateImageFilePath(longLink))
                            {
                                //就是目标
                                if (showChildWindow)
                                {
                                    ImagePreviewWindow ipw = new ImagePreviewWindow(longLink, "")
                                    {
                                        Owner = App.Current.MainWindow,
                                        WindowStartupLocation = WindowStartupLocation.CenterOwner,
                                        WindowState = WindowState.Normal,
                                        Height = 480,
                                        Width = 640,
                                    };
                                    ipw.Show();
                                    return true;
                                }
                                else
                                {
                                    switch (Globals.MainWindow.PerspectiveMode)
                                    {
                                        case Perspective.Normal:
                                        //case Perspective.Default:  // 即 Normal
                                        case Perspective.All:
                                        case Perspective.Editing:
                                        case Perspective.EditingAndPresentation:
                                            {
                                                //在主界面左工具栏的图像预览区域预览
                                                if (Globals.MainWindow.cdLeftToolsArea.ActualWidth < 100)
                                                {
                                                    Globals.MainWindow.SwitchLeftToolBarToggle();
                                                }
                                                break;
                                            }
                                        case Perspective.FullScreenEditing:
                                        case Perspective.EditingAndPreview:
                                        case Perspective.FullScreenPreview:
                                        case Perspective.CompareMode:
                                        case Perspective.MiniMode:
                                            {

                                                break;
                                            }
                                    }

                                    BitmapImage bitmapImage;
                                    Globals.MainWindow.ImagePreview.Source =
                                        bitmapImage = L.BuildBitmapImage(longLink, out Size size);  //会锁定文件：new BitmapImage(new Uri(subPath));

                                    Globals.MainWindow.ImagePreview.MaxWidth = bitmapImage.PixelWidth;
                                    Globals.MainWindow.ImagePreview.MaxHeight = bitmapImage.PixelHeight;

                                    Globals.MainWindow.imagePreviewOutBorder.Tag = Globals.MainWindow.FindWorkspaceTreeViewItem(longLink);

                                    var w = bitmapImage.Width;
                                    var h = bitmapImage.Height;
                                    var cdw = Globals.MainWindow.cdLeftToolsArea.ActualWidth - 20;
                                    var rdh = Math.Max(0,
                                        Math.Min((Globals.MainWindow.leftToolBarGrid.ActualWidth <= 10 ? 320 : Globals.MainWindow.leftToolBarGrid.ActualWidth) * h / w +
                                        Globals.MainWindow.mainTabControl.FontSize + 68,
                                        Globals.MainWindow.leftToolBarGrid.ActualHeight));
                                    Globals.MainWindow.rdLeftToolsTop.Height = new GridLength(1, GridUnitType.Star);
                                    Globals.MainWindow.rdResourcePreviewArea.Height = new GridLength(rdh);

                                    if (Globals.MainWindow.tcResourcePreviewArea.SelectedItem != Globals.MainWindow.tiImagePreview)
                                    {
                                        Globals.MainWindow.tcResourcePreviewArea.SelectedItem = Globals.MainWindow.tiImagePreview;
                                    }

                                    Globals.MainWindow.tbImageTitle.Visibility = Visibility.Collapsed;
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 预览引用的图像文件。
        /// </summary>
        /// <returns>返回图像的Title文本。（失败会返回空字符串。）</returns>
        public string PreviewLinkedImageFile(bool showChildWindow = false, string imageLinkedText = null, bool isPasting = false)
        {
            if (PreviewQuickLinkedImage(showChildWindow)) return "";

            string relativePath;
            string imageTitle = "";
            if (string.IsNullOrWhiteSpace(imageLinkedText))
            {
                var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
                var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

                var selIndex = this.editorBase.SelectionStart - line.Offset;

                int leftBracketIndex;
                int rightBracketIndex;
                if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
                {
                    leftBracketIndex = lineText.IndexOf("](", selIndex);
                    rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                    if (rightBracketIndex < 0)
                        rightBracketIndex = lineText.IndexOf(")", selIndex);
                }
                else
                {
                    leftBracketIndex = lineText.LastIndexOf("](", selIndex);

                    if (leftBracketIndex < 0)
                    {
                        if (selIndex >= 1)
                        {
                            leftBracketIndex = lineText.LastIndexOf("![", selIndex - 1);
                        }

                        if (leftBracketIndex >= 0)
                        {
                            leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 2);
                        }

                        if (leftBracketIndex >= 0)
                        {
                            rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                            if (rightBracketIndex < 0)
                                rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                        }
                        else
                        {
                            rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                            if (rightBracketIndex < 0)
                                rightBracketIndex = lineText.IndexOf(")", selIndex);
                        }
                    }
                    else
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                    }
                }

                if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
                {
                    //LMessageBox.Show("当前插入点位置处的文本不像是个对图像文件的引用！", Globals.AppName,
                    //     MessageBoxButton.OK, MessageBoxImage.Warning);
                    return "";
                }

                if (File.Exists(this.FullFilePath) == false)
                {
                    LMessageBox.Show("正在编辑的 Markdown 文件必须先保存到物理磁盘才可以查找引用！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                    return "";
                }

                var startSquareBracketIndex = lineText.LastIndexOf("[", leftBracketIndex);

                if (startSquareBracketIndex >= 0 && startSquareBracketIndex < leftBracketIndex && leftBracketIndex > 2)
                {
                    imageTitle = lineText.Substring(startSquareBracketIndex + 1, leftBracketIndex - startSquareBracketIndex - 1);
                }

                relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', '　', '\t' });

                //if (relativePath.ToLower().EndsWith(".html"))
                //    relativePath = relativePath.Substring(0, relativePath.Length - 4) + ".md";
            }
            else
            {
                Regex imageTitleReg = new Regex(@"(?<=(!\[)).*(?=\])");
                Regex relativePathReg = new Regex(@"(?<=(!\[.*\]\()).*(?=[ )])");
                var relativePathMatch = relativePathReg.Match(imageLinkedText);
                if (relativePathMatch.Success)
                {
                    relativePath = relativePathMatch.Value;
                }
                else
                {
                    LMessageBox.Show("当前插入点位置处的文本不像是个对图像文件的引用！", Globals.AppName,
                         MessageBoxButton.OK, MessageBoxImage.Warning);
                    return "";
                }

                var imageTitleMatch = imageTitleReg.Match(imageLinkedText);
                if (imageTitleMatch.Success)
                {
                    imageTitle = imageTitleMatch.Value;
                }
            }

            // 根据主题判断应显示哪个对应图像
            Regex regThemeImage = new Regex(@"\{%[^\/]{1,}%\}");
            var matchThemeImage = regThemeImage.Match(relativePath);
            if (matchThemeImage.Success)
            {
                var realName = matchThemeImage.Value.Substring(2, matchThemeImage.Length - 4);
                realName += "--" + Globals.MainWindow.ThemeText + "--";
                relativePath = relativePath.Substring(0, matchThemeImage.Index) + realName + relativePath.Substring(matchThemeImage.Index + matchThemeImage.Length);

                var regStyle = new Regex(@"[ 　\t]{1,}style=(("".*"")|('.*'))[ 　\t]?$", RegexOptions.IgnoreCase);
                var matchStyle = regStyle.Match(relativePath);
                if (matchStyle.Success)
                {
                    relativePath = relativePath.Substring(0, relativePath.Length - matchStyle.Length);
                }
            }

            var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对图像文件的引用！", Globals.AppName,
                     MessageBoxButton.OK, MessageBoxImage.Warning);
                return imageTitle;
            }

            FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
            var directory = thisFileInfo.Directory;

            int x = 0;
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                    x++;
                }
                else
                {
                    var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);

                    if (Directory.Exists(subPath) == false)
                    {
                        if (WorkspaceTreeViewItem.IsValidateImageFilePath(subPath))
                        {
                            //就是目标
                            if (showChildWindow)
                            {
                                ImagePreviewWindow ipw = new ImagePreviewWindow(subPath, imageTitle)
                                {
                                    Owner = App.Current.MainWindow,
                                    WindowStartupLocation = WindowStartupLocation.CenterOwner,
                                    WindowState = WindowState.Normal,
                                    Height = 480,
                                    Width = 640,
                                };
                                ipw.Show();//允许多张图片同时演示
                                return imageTitle;
                            }
                            else
                            {
                                switch (Globals.MainWindow.PerspectiveMode)
                                {
                                    case Perspective.Normal:
                                    //case Perspective.Default:  // 即 Normal
                                    case Perspective.All:
                                    case Perspective.Editing:
                                    case Perspective.EditingAndPresentation:
                                        {
                                            //在主界面左工具栏的图像预览区域预览
                                            if (isPasting)
                                            {
                                                if (Globals.MainWindow.cdLeftToolsArea.ActualWidth < 100 && Globals.MainWindow.cdLeftToolsArea.ActualWidth > 10)
                                                {
                                                    Globals.MainWindow.SwitchLeftToolBarToggle();
                                                }
                                            }
                                            else
                                            {
                                                if (Globals.MainWindow.cdLeftToolsArea.ActualWidth < 100)
                                                {
                                                    Globals.MainWindow.SwitchLeftToolBarToggle();
                                                }
                                            }
                                            break;
                                        }
                                    case Perspective.FullScreenEditing:
                                    case Perspective.EditingAndPreview:
                                    case Perspective.FullScreenPreview:
                                    case Perspective.CompareMode:
                                    case Perspective.MiniMode:
                                        {

                                            break;
                                        }
                                }

                                BitmapImage bitmapImage;
                                Globals.MainWindow.ImagePreview.Source =
                                    bitmapImage = L.BuildBitmapImage(subPath, out Size size);//会锁定文件：new BitmapImage(new Uri(subPath));

                                Globals.MainWindow.ImagePreview.MaxWidth = bitmapImage.PixelWidth;
                                Globals.MainWindow.ImagePreview.MaxHeight = bitmapImage.PixelHeight;

                                Globals.MainWindow.imagePreviewOutBorder.Tag = Globals.MainWindow.FindWorkspaceTreeViewItem(subPath);

                                var w = bitmapImage.Width;
                                var h = bitmapImage.Height;
                                var cdw = Globals.MainWindow.cdLeftToolsArea.ActualWidth - 20;
                                var rdh = Math.Max(0,
                                    Math.Min((Globals.MainWindow.leftToolBarGrid.ActualWidth <= 10 ? 320 : Globals.MainWindow.leftToolBarGrid.ActualWidth) * h / w +
                                    Globals.MainWindow.mainTabControl.FontSize + 68,
                                    Globals.MainWindow.leftToolBarGrid.ActualHeight));
                                Globals.MainWindow.rdLeftToolsTop.Height = new GridLength(1, GridUnitType.Star);
                                Globals.MainWindow.rdResourcePreviewArea.Height = new GridLength(rdh);

                                if (Globals.MainWindow.tcResourcePreviewArea.SelectedItem != Globals.MainWindow.tiImagePreview)
                                {
                                    Globals.MainWindow.tcResourcePreviewArea.SelectedItem = Globals.MainWindow.tiImagePreview;
                                }

                                Globals.MainWindow.tbImageTitle.Text = imageTitle;
                                if (string.IsNullOrWhiteSpace(imageTitle))
                                {
                                    Globals.MainWindow.tbImageTitle.Visibility = Visibility.Collapsed;
                                }
                                else
                                {
                                    Globals.MainWindow.tbImageTitle.Visibility = Visibility.Visible;
                                }
                                return imageTitle;
                            }
                        }

                        LMessageBox.Show("当前插入点位置处的文本不像是个对图像文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return imageTitle;
                    }
                    else
                    {
                        directory = new DirectoryInfo(subPath);
                        x++;
                    }
                }
            }

            if (x != pieces.Length - 1)
            {
                LMessageBox.Show("当前插入点位置处的文本不像是个对图像文件的引用！",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return imageTitle;
            }

            LMessageBox.Show(directory.FullName + pieces[pieces.Length - 1]);
            return imageTitle;
        }

        /// <summary>
        /// 取出文本中所有图像链接（形如 ![xxx](xxx) ）。
        /// </summary>
        /// <returns></returns>
        public List<string> GetImageLinksFromText()
        {
            return GetImageLinksFromText(EditorBase.Text);
        }

        /// <summary>
        /// 取出文本中所有图像链接（形如 ![xxx](xxx) ）。
        /// 为保证之前的脚本的兼容性才保留这个方法。其实没必要，可以直接调用 ImageLink 类中的同名方法。
        /// </summary>
        /// <param name="srcText">源文本。</param>
        public List<string> GetImageLinksFromText(string srcText)
        {
            return ImageLinkTool.GetImageLinksFromText(srcText);
        }

        /// <summary>
        /// 取出形如（ ![xxx](xxx) 的图像链接引用的图像文件的真实位置。
        /// </summary>
        /// <param name="imageLinkText"></param>
        /// <returns></returns>
        public string GetRealPathFromImagelink(string imageLinkText)
        {
            return ImageLinkTool.GetRealPathFromImagelink(imageLinkText, FullFilePath);
        }

        /// <summary>
        /// 根据当前文档中图像链接的相对路径，取出引用的图像的真实路径。
        /// </summary>
        public string GetRealPathFromRelativeImagePath(string imgRelativePath)
        {
            if (string.IsNullOrEmpty(imgRelativePath)) return "";

            return ImageLinkTool.GetRealPathFromRelativePath(imgRelativePath, FullFilePath);
        }

        /// <summary>
        /// 为方便 IronPython 脚本调用，加个中文别名。
        /// </summary>
        /// <param name="imgRelativePath"></param>
        /// <returns></returns>
        public string 取图像绝对路径(string imgRelativePath)
        {
            return ImageLinkTool.GetRealPathFromRelativePath(imgRelativePath, FullFilePath);
        }

        /// <summary>
        /// 检查当前文档中有哪些图像链接（Markdown格式）指向的图像文件不存在。
        /// </summary>
        internal FindDocumentTreeViewItem ValidateImageLinks()
        {
            try
            {
                FindDocumentTreeViewItem fdtvi = new FindDocumentTreeViewItem(this.FullFilePath, this.ShortFileName, "【无效链接】 " + this.Title) { IsExpanded = true, };

                var lines = this.editorBase.Document.Lines;
                foreach (var line in lines)
                {
                    var lineText = this.editorBase.Document.GetText(line.Offset, line.Length);
                    var regImageLink = new Regex(@"!\[.*?\]\(.*?\)");
                    var lineMatches = regImageLink.Matches(lineText);

                    foreach (Match m in lineMatches)
                    {
                        var imageLink = m.Value;
                        Regex imageTitleReg = new Regex(@"(?<=(!\[)).*(?=\])");
                        Regex relativePathReg = new Regex(@"(?<=(!\[.*\]\()).*(?=[ )])");
                        var imageTitleMatch = imageTitleReg.Match(imageLink);
                        var relativePathMatch = relativePathReg.Match(imageLink);

                        if (relativePathMatch.Success && imageTitleMatch.Success)
                        {
                            var imageTitle = imageTitleMatch.Value;
                            var relativePath = relativePathMatch.Value;
                            var imagePath = ConvertRelativeImageLinkPathToFull(relativePath, this.FullFilePath);
                            if (File.Exists(imagePath) == false)
                            {
                                FindLineTreeViewItem ftvi = new FindLineTreeViewItem(this.FullFilePath, this.ShortFileName, line.LineNumber,
                                    relativePathMatch.Index, relativePath.Length, lineText,
                                    Brushes.Black, FindLineTreeViewItem.ItemType.Normal);
                                fdtvi.Items.Add(ftvi);
                            }
                        }
                    }
                }

                return fdtvi;
            }
            catch
            {
                return null;
            }
        }

        public static string ConvertRelativeImageLinkPathToFull(string relativeImagePath, string markdownFilePath)
        {
            if (string.IsNullOrWhiteSpace(relativeImagePath)) return null;

            var pieces = relativeImagePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0) return null;

            FileInfo thisFileInfo = new FileInfo(markdownFilePath);
            var directory = thisFileInfo.Directory;

            var tailPath = "";
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                }
                else
                {
                    tailPath += s;
                    tailPath += "\\";
                }
            }

            tailPath = tailPath.Trim(new char[] { '\\', });
            var headPath = directory.FullName;
            if (headPath.EndsWith("\\") == false) headPath += "\\";

            return headPath + tailPath;
        }

        private void MiRedo_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Redo();
        }

        public void 重做() { this.editorBase.Redo(); }

        private void MiUndo_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Undo();
        }

        public void 撤销() { this.editorBase.Undo(); }

        private void MiPaste_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Paste();
        }

        public void 粘贴() { this.Paste(); }

        private void MiCopy_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Copy();
        }

        public void 复制() { this.editorBase.Copy(); }

        private void MiCut_Click(object sender, RoutedEventArgs e)
        {
            Globals.MainWindow.Cut();
        }

        public void 剪切() { this.editorBase.Cut(); }

        private bool isCompareAreaEditor = false;
        /// <summary>
        /// 是否对照区。对照区不能双击预览。
        /// </summary>
        public bool IsCompareAreaEditor
        {
            get { return isCompareAreaEditor; }
        }

        public bool 是否对照区编辑器
        {
            get { return IsCompareAreaEditor; }
        }

        /// <summary>
        /// 双击标头编译当前文档并预览。
        /// </summary>
        private void HeaderTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (this.IsCompareAreaEditor) return;
            if (e.ClickCount == 2)
            {
                this.CompileAndPreviewHtml();
            }
        }

        /// <summary>
        /// 编译并调用外部浏览器预览。
        /// </summary>
        private void MiCallSystemDefaultWebBrowserPreview_Click(object sender, RoutedEventArgs e)
        {
            this.CompileAndPreviewHtml(true);
        }

        /// <summary>
        /// 编译当前 Markdown 文档并在右工具栏中预览。
        /// </summary>
        void miCompileAndPreview_Click(object sender, RoutedEventArgs e)
        {
            this.CompileAndPreviewHtml();
        }

        void closeBorder_MouseEnter(object sender, MouseEventArgs e)
        {
            this.closeBorder.Background = Brushes.LightGray;
        }

        void closeBorder_MouseLeave(object sender, MouseEventArgs e)
        {
            this.closeBorder.Background = Brushes.Transparent;
        }

        /// <summary>
        /// 向编辑区拖入图像文件时，自动复制到图像资源文件夹并自动生成链接。
        /// </summary>
        void MarkdownEditor_Drop(object sender, DragEventArgs e)
        {
            Array dragDataArray = ((System.Array)e.Data.GetData(DataFormats.FileDrop));
            if (dragDataArray == null || dragDataArray.Length <= 0) return;

            var filenames = new System.Collections.Specialized.StringCollection();

            foreach (string s in dragDataArray)
            {
                filenames.Add(s);
            }

            if (TryToDropResourcesFiles(filenames) == string.Empty) return;
        }

        private List<TextCommandItem> currentTextCommands = null;

        private int currentTextCommandIndex = -1;

        /// <summary>
        /// 处理模拟 Vim 命令模式下的命令。
        /// </summary>
        private void MarkdownEditor_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            if (this.EditingMode == EditingMode.VimMode)
            {
                e.Handled = true;//如果是命令，阻止其通过。
                RunTextCommand(TextCommandManager.InputedCommandText, e.Text);
            }
        }

        private void RunTextCommand(string prefixCommandText, string commandText)
        {
            List<TextCommandItem> matchedTextCommands;
            var exeResult = TextCommandManager.TryToRun(prefixCommandText, commandText, out matchedTextCommands);

            if (exeResult == "完成")
            {
                Globals.MainWindow.tbTextCommand.TextDecorations = TextDecorations.Strikethrough;
                Globals.MainWindow.tbTextCommandResult.Foreground = Brushes.DarkGreen;
                Globals.MainWindow.tbTextCommandPrompt.Text = "";
            }
            else
            {
                Globals.MainWindow.tbTextCommand.TextDecorations = null;
                Globals.MainWindow.tbTextCommandResult.Foreground = Brushes.Black;
            }

            //注意顺序
            Globals.MainWindow.tbTextCommand.Text = prefixCommandText + commandText;

            if (matchedTextCommands.Count < 1)
            {
                currentTextCommands = null;
                //无此命令
                Globals.MainWindow.tbTextCommandDescription.Text = "--";
                Globals.MainWindow.tbTextCommandResult.Text = "无此命令！";
            }
            else
            {
                currentTextCommands = matchedTextCommands;
                //有一个以上的匹配命令
                var win = Globals.MainWindow;
                win.tbTextCommandDescription.Text = matchedTextCommands[0].Description;
                currentTextCommandIndex = 0;
                win.tbTextCommandResult.Text = exeResult;
                win.spanOfMatchedCommandTextCount.Inlines.Clear();
                win.spanOfMatchedCommandTextCount.Inlines.Add(1 + "/" + matchedTextCommands.Count.ToString());
            }
        }

        private static List<string> singleTagsList = new List<string>();

        void MarkdownEditor_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key != Globals.VimKey)
            {
                vimKeyUpedCount = 0;
            }

            var isLShift = ((Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0);
            var isRShift = ((Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0);

            if (this.EditingMode == EditingMode.VimMode)
            {
                //Vim 命令模式下禁止其它操作，但必须保留消息，否则所有命令都不起作用的。
                //e.Handled = true;
                switch (e.Key)
                {
                    case Key.Up:
                    case Key.Down:
                    case Key.Right:
                    case Key.Left:
                        {
                            e.Handled = true;         //禁用光标键，因为会造成逻辑问题。
                            break;
                        }
                    case Key.Tab:
                        {
                            //切换显示匹配的各条命令。
                            if (currentTextCommands != null && currentTextCommands.Count > 0)
                            {
                                if (isLShift || isRShift)
                                {
                                    if (currentTextCommandIndex > 0 && currentTextCommandIndex < currentTextCommands.Count)
                                    {
                                        currentTextCommandIndex--;
                                        var win = Globals.MainWindow;
                                        win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                        win.spanOfMatchedCommandTextCount.Inlines.Add(currentTextCommandIndex + 1 + "/" + currentTextCommands.Count);
                                        win.tbTextCommandPrompt.Text = currentTextCommands[currentTextCommandIndex].CommandText;
                                        win.tbTextCommandDescription.Text = currentTextCommands[currentTextCommandIndex].Description;
                                        win.tbTextCommandResult.Text = "回车执行";
                                    }
                                }
                                else
                                {
                                    if (currentTextCommandIndex >= 0 && currentTextCommandIndex < currentTextCommands.Count - 1)
                                    {
                                        currentTextCommandIndex++;
                                        var win = Globals.MainWindow;
                                        win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                        win.spanOfMatchedCommandTextCount.Inlines.Add(currentTextCommandIndex + 1 + "/" + currentTextCommands.Count);
                                        win.tbTextCommandPrompt.Text = currentTextCommands[currentTextCommandIndex].CommandText;
                                        win.tbTextCommandDescription.Text = currentTextCommands[currentTextCommandIndex].Description;
                                        win.tbTextCommandResult.Text = "回车执行";
                                    }
                                }
                            }

                            e.Handled = true;
                            break;
                        }
                    case Key.Return:
                        {
                            //强制执行第一条匹配的命令
                            if (currentTextCommands != null && currentTextCommands.Count > 0 &&
                                currentTextCommandIndex >= 0 && currentTextCommandIndex < currentTextCommands.Count)
                            {
                                var cmdItem = currentTextCommands[currentTextCommandIndex];
                                var result = cmdItem.Run(cmdItem);
                                if (result)
                                {
                                    TextCommandManager.InputedCommandText = "";
                                    var win = Globals.MainWindow;
                                    win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                    win.tbTextCommand.Text = cmdItem.CommandText;
                                    win.tbTextCommandResult.Text = "完成";
                                    win.tbTextCommandResult.Foreground = Brushes.DarkGreen;
                                    win.tbTextCommand.TextDecorations = TextDecorations.Strikethrough;
                                }
                                else
                                {
                                    var win = Globals.MainWindow;
                                    win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                    win.tbTextCommandResult.Text = "无此命令";
                                    win.tbTextCommandResult.Foreground = Brushes.Black;
                                    win.tbTextCommand.TextDecorations = null;
                                }
                            }
                            e.Handled = true;
                            break;
                        }
                    case Key.Back:
                        {
                            var inputedCommandText = TextCommandManager.InputedCommandText;
                            var length = inputedCommandText.Length;
                            if (length > 0)
                            {
                                var win = Globals.MainWindow;
                                TextCommandManager.InputedCommandText = inputedCommandText.Substring(0, length - 1);
                                if (string.IsNullOrWhiteSpace(TextCommandManager.InputedCommandText))
                                {
                                    currentTextCommands = null;
                                }
                                else
                                {
                                    currentTextCommands = TextCommandManager.GetMatchedTextCommands(TextCommandManager.InputedCommandText);
                                }

                                if (currentTextCommands == null || currentTextCommands.Count <= 0)
                                {
                                    currentTextCommandIndex = -1;
                                    win.tbTextCommand.Text = TextCommandManager.InputedCommandText;
                                    win.tbTextCommandResult.Text = "无此命令";                      //有多个符合条件的命令。
                                    win.tbTextCommandDescription.Text = "";
                                    win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                }
                                else
                                {
                                    currentTextCommandIndex = 0;
                                    win.tbTextCommand.Text = TextCommandManager.InputedCommandText;
                                    win.tbTextCommandResult.Text = "等待继续输入...";                      //有多个符合条件的命令。
                                    win.tbTextCommandDescription.Text = currentTextCommands[0].Description;
                                    win.spanOfMatchedCommandTextCount.Inlines.Clear();
                                    win.spanOfMatchedCommandTextCount.Inlines.Add(1 + "/" + currentTextCommands.Count);
                                }
                            }
                            e.Handled = true;
                            break;
                        }
                }
                return;
            }

            bool isCtrl = false;
            if ((Keyboard.GetKeyStates(Key.RightCtrl) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftCtrl) & KeyStates.Down) > 0)
            {
                isCtrl = true;
            }

            bool isShift = false;
            if ((Keyboard.GetKeyStates(Key.RightShift) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftShift) & KeyStates.Down) > 0)
            {
                isShift = true;
            }

            bool isAlt = false;
            if ((Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) > 0 || (Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Down) > 0)
            {
                isAlt = true;
            }

            // 普通模式下的处理。

            if (this.EditingMode == EditingMode.NormalMode)
            {
                switch (e.Key)
                {
                    case Key.V:
                        {
                            if (isCtrl && isAlt == false)
                            {
                                if (isShift)
                                {
                                    //粘贴代码
                                    PasteCode();
                                }
                                else
                                {
                                    try
                                    {
                                        var clipText = Clipboard.GetText();
                                        if (string.IsNullOrEmpty(clipText) == false)
                                        {
                                            var ltxt = this.editorBase.LeftText;
                                            if (ltxt == "^ ")
                                            {
                                                clipText = Text.FormatLineSplitter(clipText).Replace("\r\n", "\r\n^ ").Replace("^ \r\n", "\r\n");

                                                clipText = Regex.Replace(clipText, @"([ 　\t]{0,}\r\n[ 　\t]{0,}){1,}", "  \r\n\r\n").Trim(new char[] { ' ', '　', '\t', '\r', '\n', '^' }) + "\r\n\r\n";
                                                this.editorBase.Paste(clipText);
                                            }
                                            else
                                            {
                                                this.editorBase.Paste();
                                            }
                                        }
                                        else
                                        {
                                            //如果是粘贴图像，粘贴后不再继续操作。
                                            var result = TryToPasteResourceFileLink();
                                            if (result == string.Empty)
                                            {
                                                e.Handled = true;
                                                break;
                                            }
                                            // 啰嗦
                                            // LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        LMessageBox.ShowWarning("未能从剪贴板中取得有效数据，请尝试重新复制并粘贴。异常消息是：" + ex.Message);
                                    }
                                }
                                e.Handled = true;
                                return;
                            }
                            break;
                        }
                    case Key.F4:
                        {
                            if (isCtrl && isShift == false && isAlt == false)
                            {
                                Close();
                                e.Handled = true;
                                return;
                            }
                            else
                            {
                                if (!isShift && !isAlt)
                                {
                                    //单F4，生成选择题标签集
                                    if (IsExamEnabled) editorBase.InsertChoiceQuestionTags();
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.W:
                        {
                            if (isCtrl && isShift == false && isAlt == false)
                            {
                                Close();
                                e.Handled = true;
                                return;
                            }
                            break;
                        }
                    case Key.F2:
                        {
                            if (isCtrl == false && isAlt == false)
                            {
                                var leftMark = isShift ? "[" : "【";
                                var rightMark = isShift ? "]" : "】";

                                if (WrapSelection(leftMark, rightMark) == false)  // 优先考虑使用封装方法——这个方法比较靠谱。
                                {
                                    if (this.EditorBase.SelectedText.Contains("\r\n")) return;

                                    if (this.EditorBase.SelectionLength <= 0)
                                    {
                                        //如果没选中文本，说明是打算输入一个新填空项。
                                        //这时候直接定位到两个括号中间好。
                                        if (isShift == false)
                                        {
                                            this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
                                        }
                                        else
                                        {
                                            this.editorBase.SelectedText = "[" + this.EditorBase.SelectedText + "]";
                                        }
                                        this.editorBase.Select(this.editorBase.SelectionStart + 1, 0);
                                    }
                                    else
                                    {
                                        //如果已经选定了一些文本，说明是打算将这些文本作为一个填空项，
                                        //此时一般没必要再修改文本，直接定位到右括弧后面，可以接着输入，这样更方便些。
                                        if (isShift == false)
                                        {
                                            this.editorBase.SelectedText = "【" + this.EditorBase.SelectedText + "】";
                                        }
                                        else
                                        {
                                            this.editorBase.SelectedText = "[" + this.EditorBase.SelectedText + "]";
                                        }
                                        this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                    }
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.F8:
                        {
                            if (!isCtrl && !isAlt && isShift)
                            {
                                if (IsExamEnabled)
                                {
                                    this.editorBase.InsertJudgeQuestionTags("正确");
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.F9:
                        {
                            if (!isCtrl && !isAlt && isShift)
                            {
                                if (IsExamEnabled)
                                {
                                    this.editorBase.InsertJudgeQuestionTags("错误");
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.P:
                        {
                            if (isCtrl && !isAlt)
                            {
                                if (isShift)
                                {
                                    Globals.MainWindow.PrintPreview();
                                }
                                else
                                {
                                    Globals.MainWindow.PrintDocument();
                                }
                                e.Handled = true;
                                return;
                            }
                            break;
                        }
                    case Key.R:
                        {
                            if (isAlt == false && isCtrl && isShift == false)
                            {
                                this.editorBase.WordWrap = !this.editorBase.WordWrap;
                                if (Globals.MainWindow != null)
                                {
                                    Globals.MainWindow.RefreshTextAutoWrapToStatusBar();
                                    e.Handled = true;
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.F:
                        {
                            if (isCtrl)
                            {
                                if (isAlt)
                                {
                                    //Ctrl+Alt+F
                                    //移动到MainWindow//2017年1月10日
                                }
                                else
                                {
                                    //Ctrl+F                                    
                                    //miFindInActiveDocument_Click(sender, e);
                                    //此方法也曾经被移动到MainWindow。
                                    //但问题在于：这样子做会导致在Html预览页中按Ctrl+F无法弹出查找框来查找Html页面中的文本，
                                    //            又或者会导致在Html预览页面中按Ctrl+F时也弹出Markdown查找框——这显得十分累赘。
                                    if (Globals.MainWindow.rdFindAndReplace.ActualHeight <= 20)
                                    {
                                        Globals.MainWindow.rdFindAndReplace.Height = new GridLength(0, GridUnitType.Auto);
                                        Globals.MainWindow.cmbFindText.UpdateLayout();
                                    }

                                    var editor = Globals.MainWindow.ActivedEditor;
                                    if (editor != null && editor.EditorBase.SelectedText.Contains('\r') == false &&
                                        editor.EditorBase.SelectedText.Contains('\n') == false && editor.EditorBase.SelectionLength > 0)
                                    {
                                        //如果允许多行，很容易导致崩溃。
                                        Globals.MainWindow.cmbFindText.Text = editor.EditorBase.SelectedText;
                                    }
                                    //else
                                    //{
                                    //    Globals.MainWindow.cmbFindText.Text = "";
                                    //}
                                    //为什么不样做？因为用户随时会再进行下一次检索，太自动化会出问题。

                                    //if (Globals.MainWindow.mainTabControl.Items.Count <= 0)
                                    //{
                                    //    Globals.MainWindow.cmbSearchArea.SelectedIndex = 2;//全工作区查找
                                    //}
                                    //else
                                    //{
                                    Globals.MainWindow.cmbSearchArea.SelectedIndex = 0;//当前文档查找
                                                                                       //}

                                    Globals.MainWindow.cmbFindText.Focus();
                                    if (Globals.MainWindow.cmbFindText.Text.Length > 0)
                                    {
                                        Globals.MainWindow.Find();
                                    }

                                    Globals.MainWindow.tcFindAndReplace.SelectedIndex = 0;

                                    //e.Handled = true;
                                    return;
                                }
                            }
                            break;
                        }
                    case Key.OemPeriod:  // 句点键
                        {
                            if (isShift && !isCtrl && !isAlt)
                            {
                                try
                                {
                                    var doc = editorBase.Document;
                                    var edit = editorBase;
                                    var textArea = edit.TextArea;

                                    var oldSelStart = edit.SelectionStart;

                                    var line = doc.GetLineByOffset(oldSelStart);
                                    var prefixText = doc.GetText(line.Offset, oldSelStart - line.Offset);
                                    var nextText = doc.GetText(oldSelStart, line.EndOffset - oldSelStart);
                                    string tagName;
                                    if (IsEndOfHtmlTag(prefixText, out tagName) && nextText.StartsWith($"</{tagName}>") == false)
                                    {
                                        var reg = new Regex(@"[a-zA-Z]{1,}");
                                        var match = reg.Match(tagName);
                                        var lastLeftMarkIndex = prefixText.LastIndexOf('<');
                                        if (lastLeftMarkIndex >= 0)
                                        {
                                            prefixText = prefixText.Substring(lastLeftMarkIndex);
                                            if (prefixText.StartsWith("<" + tagName) && match.Success &&
                                                prefixText.EndsWith(":") == false && prefixText.EndsWith("：") == false)
                                            {
                                                if (singleTagsList.Contains(tagName.ToLower()) == false)
                                                {
                                                    // <br><hr><img><input><param><meta><link> 这些是单标签，不需要添加“</xxx>”这样的尾标签。
                                                    doc.Replace(edit.SelectionStart, 0, $"></{tagName}>");
                                                    edit.Select(oldSelStart + 1, 0);
                                                    e.Handled = true;
                                                }
                                            }
                                        }
                                    }
                                    return;
                                }
                                catch (Exception ex)
                                {
                                    LMessageBox.Show(ex.Message);
                                    return;
                                }
                            }
                            break;
                        }
                }

                if (e.Key != Key.Tab)    // 注意：关于 Tab 调用脚本是放在 MarkdownEditorBase 类中的，不然会干扰 Tab 键的其它功能pw。
                {
                    // 处理 Python 脚本快捷键（用快捷键调用 Python 脚本）
                    // ★关于 Python 脚本的快捷键，不应覆盖 LEM 本身的快捷键[尝试过，效果不好]。
                    int runedScriptsCount = 0;
                    foreach (var item in Globals.MainWindow.lbxPythonScripts.Items)
                    {
                        var psi = item as PythonScriptListItem;
                        if (psi == null) continue;

                        if (psi.RunScriptByShortCut(isShift, isAlt, isCtrl, e.Key)) runedScriptsCount++;
                    }

                    foreach (var item in Globals.MainWindow.lbxWorkspacePythonScripts.Items)
                    {
                        var psi = item as PythonScriptListItem;
                        if (psi == null) continue;

                        if (psi.RunScriptByShortCut(isShift, isAlt, isCtrl, e.Key)) runedScriptsCount++;
                    }

                    if (runedScriptsCount > 0)
                    {
                        e.Handled = true;
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 使用指定的“左”“右”字符串对当前活动编辑器中选取的文本片段进行“封装”。
        /// ★ 此方法支持（按Alt）垂直选取的文本片段。
        /// </summary>
        /// <param name="leftWrapText">“左”字符串。</param>
        /// <param name="rightWrapText">“右”字符串。</param>
        public bool WrapSelection(string leftWrapText = "", string rightWrapText = "")
        {
            if (string.IsNullOrEmpty(leftWrapText) && string.IsNullOrEmpty(rightWrapText)) return false;

            try
            {
                var segments = this.EditorBase.TextArea.Selection.Segments;

                List<ICSharpCode.AvalonEdit.Editing.SelectionSegment> segmentsList = new List<ICSharpCode.AvalonEdit.Editing.SelectionSegment>();
                foreach (var segment in segments)
                {
                    segmentsList.Add(segment);
                }

                if (segmentsList.Count <= 0)
                {
                    // 只有插入点，没有选中任何文本，此时不可能是多行。
                    var sel = this.EditorBase.SelectionStart;
                    var selLength = this.EditorBase.SelectionLength;
                    this.EditorBase.BeginChange();
                    this.EditorBase.Document.Insert(sel + selLength, rightWrapText);
                    this.EditorBase.Document.Insert(sel, leftWrapText);
                    this.EditorBase.EndChange();
                    this.EditorBase.Select(sel + leftWrapText.Length, selLength);
                }
                else
                {
                    if (segmentsList.Count == 1 && this.EditorBase.TextArea.Selection.IsMultiline)            // 常规选取方式，选中多行文本。
                    {
                        LMessageBox.ShowWarning("当选中的文本包括换行时，此操作不能执行！");
                        return false;
                    }

                    // 垂直选取文本，存在 1－n 个片段。
                    this.EditorBase.BeginChange();
                    for (int i = segmentsList.Count - 1; i >= 0; i--)
                    {
                        var segment = segmentsList[i];
                        var sel = segment.StartOffset;
                        var selLength = segment.Length;
                        this.EditorBase.Document.Insert(sel + selLength, rightWrapText);
                        this.EditorBase.Document.Insert(sel, leftWrapText);
                        //this.Select(sel + leftWrapText.Length, selLength);  // 不需要考虑选取区域后移这个问题。AvalonEdit 本身已提供支持。
                    }
                    this.EditorBase.EndChange();
                }
                return true;
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
                return false;
            }
        }

        public bool 封装选择区(string leftWrapText = "", string rightWrapText = "")
        {
            return WrapSelection(leftWrapText, rightWrapText);
        }

        /// <summary>
        /// 注意，它与 MarkdownEditorBase 类下的 IsEndWithHtmlTag() 方法的计算方法是不同的。
        /// </summary>
        /// <param name="prefixText"></param>
        /// <param name="tagName"></param>
        /// <returns></returns>
        private static bool IsEndOfHtmlTag(string prefixText, out string tagName)
        {
            if (string.IsNullOrWhiteSpace(prefixText) || prefixText.EndsWith("/") || prefixText.EndsWith(">"))
            {
                tagName = ""; return false;
            }

            //这里用正则太复杂，要考虑反向、Attribute 值带尖括号等特殊情况，
            //还是就简单点好了——反正平时我也用不到这些特殊情况。

            var preStartIndex = prefixText.LastIndexOf('<');
            var preEndIndex = prefixText.LastIndexOf('>');

            if (preEndIndex > preStartIndex) { tagName = ""; return false; }   // <someTag >kdk>   这时候不就加结束标签

            var nextCharIndex = preStartIndex + 1;
            if (nextCharIndex >= prefixText.Length) { tagName = ""; return false; }

            var tail = prefixText.Substring(nextCharIndex);
            if (string.IsNullOrWhiteSpace(tail)) { tagName = ""; return false; }

            var indexOfSpace = tail.IndexOf(' ');
            var tagSpan = tail;
            if (indexOfSpace > 0)
            {
                tagSpan = tail.Substring(0, indexOfSpace);
            }

            Regex reg = new Regex(@"^[a-zA-Z][a-zA-Z0-9]{0,}$");
            if (reg.Match(tagSpan).Success)
            {
                tagName = tagSpan;
                return true;
            }

            tagName = "";
            return false;
        }

        private enum ResourceType { MarkdownFile, ImageFile, SoundFile, VedioFile };

        /// <summary>
        /// 向编辑区拖入图像、音乐、视频文件时，自动尝试复制图像、音乐、视频文件到对应资源目录下并自动生成链接文本。
        /// </summary>
        /// <param name="dropList">拖动的图像、音乐、视频文件的列表。</param>
        public string TryToDropResourcesFiles(System.Collections.Specialized.StringCollection dropList, bool refreshWorkspace = false)
        {
            try
            {
                if (dropList.Count > 0)
                {
                    var vedioFilesCount = 0;
                    var regBlanks = new Regex(@"[ 　\t]");
                    var sbIgnoredFiles = new StringBuilder();
                    var ignoredFilesCount = 0;

                    foreach (string fullPath in dropList)
                    {
                        if (regBlanks.Match(fullPath).Success)
                        {
                            sbIgnoredFiles.Append(fullPath);
                            sbIgnoredFiles.Append("\r\n");
                            ignoredFilesCount++;
                            continue;
                        }

                        var resourceType = ResourceType.MarkdownFile;
                        string shortResourceDirectoryName = "";
                        if (WorkspaceTreeViewItem.IsValidateImageFilePath(fullPath))
                        {
                            resourceType = ResourceType.ImageFile;
                            shortResourceDirectoryName = "Images~";
                        }
                        else if (WorkspaceTreeViewItem.IsValidateSoundFilePath(fullPath))
                        {
                            resourceType = ResourceType.SoundFile;
                            shortResourceDirectoryName = "Sounds~";
                        }
                        else if (WorkspaceTreeViewItem.IsValidateVedioFilePath(fullPath))
                        {
                            resourceType = ResourceType.VedioFile;
                            shortResourceDirectoryName = "Vedios~";
                        }
                        else
                        {
                            InsertAbsoluteFileOrDirectoryPathLink(fullPath);
                            continue;
                        }

                        switch (resourceType)
                        {
                            case ResourceType.ImageFile:
                                {
                                    if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
                                    {
                                        Directory.CreateDirectory(this.ImageResourceDirectoryPath);
                                    }
                                    break;
                                }
                            case ResourceType.SoundFile:
                                {
                                    if (Directory.Exists(this.SoundResourceDirectoryPath) == false)
                                    {
                                        Directory.CreateDirectory(this.SoundResourceDirectoryPath);
                                    }
                                    break;
                                }
                            case ResourceType.VedioFile:
                                {
                                    if (Directory.Exists(this.VedioResourceDirectoryPath) == false)
                                    {
                                        Directory.CreateDirectory(this.VedioResourceDirectoryPath);
                                    }
                                    break;
                                }
                        }

                        var shortFileInfo = new FileInfo(fullPath);
                        var shortFileName = shortFileInfo.Name;
                        string destFilePath;

                        switch (resourceType)
                        {
                            case ResourceType.ImageFile:
                                {
                                    destFilePath = this.ImageResourceDirectoryPath + shortFileName;
                                    break;
                                }
                            case ResourceType.SoundFile:
                                {
                                    destFilePath = this.SoundResourceDirectoryPath + shortFileName;
                                    break;
                                }
                            case ResourceType.VedioFile:
                            default:
                                {
                                    //LMessageBox.Show("要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前，不支持此功能。",
                                    //    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                                    destFilePath = this.VedioResourceDirectoryPath + shortFileName;
                                    break;
                                    // return "";
                                }
                        }

                        if (fullPath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()))
                        {
                            //如果资源文件就在工作区内，则生成相对引用链接而不是复制资源文件。

                            var srcPath = fullPath; // .ToLower();  不能加，否则 Linux 下会出错。
                            var workspacePath = Globals.PathOfWorkspace.ToLower();

                            //思路：先找出共同的、最接近的祖先级目录
                            var separator = new char[] { '\\', '/' };
                            string[] src = srcPath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                            string[] dest = destFilePath/*.ToLower()*/.Split(separator, StringSplitOptions.RemoveEmptyEntries);

                            var minIndex = Math.Min(src.Length, dest.Length) - 1;

                            if (minIndex < 0) return string.Empty;

                            int aIndex = -1;
                            for (int i = 0; i <= minIndex; i++)
                            {
                                if (src[i] != dest[i])
                                {
                                    aIndex = i;
                                    break;
                                }
                            }

                            //生成相对路径的前半部分，类似（../../）这样子。

                            string[] file = this.FullFilePath.Split(separator, StringSplitOptions.RemoveEmptyEntries);
                            string header;
                            string tail;

                            if (aIndex < 0)
                            {
                                header = "";//就已经在同一个文件的资源目录中
                                tail = (this.shortFileName.ToLower().EndsWith(".md") ? this.shortFileName.Substring(0, this.shortFileName.Length - 3) : this.shortFileName)
                                    + $"~/{shortResourceDirectoryName}/" + shortFileName;
                            }
                            else
                            {
                                header = BuildHtmlRefText(file.Length - aIndex - 1);
                                //生成相对路径的后半部分。
                                StringBuilder sb = new StringBuilder();
                                for (int i = aIndex; i < src.Length; i++)
                                {
                                    sb.Append(src[i] + "/");
                                }

                                tail = sb.ToString();
                                if (tail.EndsWith("/"))
                                {
                                    tail = tail.Substring(0, tail.Length - 1);
                                }
                            }

                            //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='" + header + tail + "' />";

                            //var shortTitle = MainWindow.FormatDocumentTitle(shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length));
                            var shortTitle = MainWindow.FormatDocumentTitle(WorkspaceTreeViewItem.GetCommentShortName(shortFileInfo.FullName));

                            if (shortTitle.EndsWith(shortFileInfo.Extension))
                            {
                                shortTitle = shortTitle.Substring(0, shortTitle.Length - shortFileInfo.Extension.Length);
                            }

                            switch (resourceType)
                            {
                                case ResourceType.ImageFile:
                                    {
                                        var msgSpans = new List<MessageTextSpan>();
                                        msgSpans.Add(new MessageTextSpan() { Text = "要把 ", });
                                        msgSpans.Add(new MessageTextSpan() { Text = "当前插入点位置处的图像链接", FontWeight = FontWeights.Bold, Foreground = Brushes.SaddleBrown, });
                                        msgSpans.Add(new MessageTextSpan() { Text = " 替换为 ", });
                                        msgSpans.Add(new MessageTextSpan() { Text = "对这个图像文件的链接", FontWeight = FontWeights.Bold, Foreground = Brushes.SaddleBrown, });
                                        msgSpans.Add(new MessageTextSpan() { Text = " 吗？", });

                                        var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
                                        var lineText = this.editorBase.Document.GetText(line.Offset, line.Length);
                                        var pieces = Utils.Text.SplitToPiecesByRegex(@"!\[.*?\]\(.*?\)", lineText);
                                        Utils.TextPiece destPiece = null;
                                        var caretOffset = this.editorBase.SelectionStart - line.Offset;
                                        foreach (var piece in pieces)
                                        {
                                            if (piece.StartIndex < caretOffset && caretOffset < piece.EndIndex)
                                            {
                                                destPiece = piece;
                                                break;
                                            }
                                        }

                                        if (destPiece != null)
                                        {
                                            var answer = LMessageBox.Show(msgSpans, Globals.AppName,
                                                 MessageBoxButton.OKCancel, MessageBoxImage.Question);
                                            if (answer != MessageBoxResult.OK) return "";

                                            this.editorBase.Select(line.Offset + destPiece.StartIndex, destPiece.Length);
                                        }

                                        if (dropList.Count == 1)
                                        {
                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                                        }
                                        else
                                        {
                                            // 从操作逻辑上来看，这个要打开对应的工作区内资源文件夹才会发生。
                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")\r\n";
                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }
                                        break;
                                    }
                                case ResourceType.SoundFile:
                                    {
                                        if (header.StartsWith("../") || header.StartsWith("./../"))
                                        {
                                            LMessageBox.ShowWarning("<embed>标签不支持跨越上层目录的相对路径。请注意此链接可以添加，但在网页中无效！");
                                        }

                                        if (dropList.Count == 1)
                                        {
                                            // this.editorBase.SelectedText = "<div class=\"sound\"><embed class=\"player\" src=\"" + header + tail + "\" autoplay=\"false\"/></div>";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                                        }
                                        else
                                        {
                                            // 从操作逻辑上来看，这个要打开对应的工作区内资源文件夹才会发生。
                                            //this.editorBase.SelectedText = "<div class=\"sound\"><embed class=\"player\" src=\"" + header + tail + "\" autoplay=\"false\"/></div>\r\n";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }

                                        break;
                                    }
                                case ResourceType.VedioFile:
                                    {
                                        if (header.StartsWith("../") || header.StartsWith("./../"))
                                        {
                                            LMessageBox.ShowWarning("<embed>标签不支持跨越上层目录的相对路径。请注意此链接可以添加，但在网页中无效！");
                                        }

                                        if (dropList.Count == 1)
                                        {
                                            // this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width =\"250\" height =\"180\"></embed>";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                                        }
                                        else
                                        {
                                            // 从操作逻辑上来看，这个要打开对应的工作区内资源文件夹才会发生。
                                            // this.editorBase.SelectedText = "<embed src=\"" + header + tail + "\" width =\"250\" height =\"180\"></embed>\r\n";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + header + tail + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }
                                        //LMessageBox.Show("要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前，不支持此功能。",
                                        //    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);

                                        break;
                                    }
                            }
                        }
                        else
                        {
                            //如果不在工作区内，则直接将文件复制到工作区内对应资源文件目录。
                            var destFileInfo = new FileInfo(destFilePath);
                            var destShortPinYinName = ChinesePinYin.ToChinesePinYinText(destFileInfo.Name.Substring(0, destFileInfo.Name.Length - destFileInfo.Extension.Length));
                            var destShortCommentName = destFileInfo.Name;
                            var oldExtension = destFileInfo.Extension;
                            shortFileName = destShortPinYinName + oldExtension;
                            destFilePath = destFileInfo.DirectoryName + "\\" + shortFileName;

                            var shouldAddEntry = false;
                            if (File.Exists(destFilePath) == false)
                            {
                                shouldAddEntry = true;
                                if (Directory.Exists(destFileInfo.DirectoryName) == false)
                                {
                                    Directory.CreateDirectory(destFileInfo.DirectoryName);
                                }

                                File.Copy(fullPath, destFilePath);

                                destFileInfo = new FileInfo(destFilePath);
                                var commentTextFilePath = destFileInfo.Directory.FullName + "\\" + "~.txt";

                                if (File.Exists(commentTextFilePath))
                                {
                                    using (var stream = File.AppendText(commentTextFilePath))
                                    {
                                        stream.WriteLine($"{destShortPinYinName}{oldExtension}|{destShortCommentName}");
                                    }
                                }
                                else
                                {
                                    using (var stream = File.CreateText(commentTextFilePath))
                                    {
                                        stream.WriteLine($"{destShortPinYinName}{oldExtension}|{destShortCommentName}");
                                    }
                                }
                            }

                            //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + "' />";

                            //var shortTitle = shortImageFileInfo.Name.Substring(0, shortImageFileInfo.Name.Length - shortImageFileInfo.Extension.Length);
                            var shortTitle = MainWindow.FormatDocumentTitle(WorkspaceTreeViewItem.GetCommentShortName(shortFileInfo.FullName));

                            if (shortTitle.EndsWith(shortFileInfo.Extension))
                            {
                                shortTitle = shortTitle.Substring(0, shortTitle.Length - shortFileInfo.Extension.Length);
                            }

                            switch (resourceType)
                            {
                                case ResourceType.ImageFile:
                                    {
                                        if (shouldAddEntry)
                                            Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.ImageResourceDirectoryPath, destFilePath);

                                        var msgSpans = new List<MessageTextSpan>();
                                        msgSpans.Add(new MessageTextSpan() { Text = "要把 ", });
                                        msgSpans.Add(new MessageTextSpan() { Text = "当前插入点位置处的图像链接", FontWeight = FontWeights.Bold, Foreground = Brushes.SaddleBrown, });
                                        msgSpans.Add(new MessageTextSpan() { Text = " 替换为 ", });
                                        msgSpans.Add(new MessageTextSpan() { Text = "对这个图像文件的链接", FontWeight = FontWeights.Bold, Foreground = Brushes.SaddleBrown, });
                                        msgSpans.Add(new MessageTextSpan() { Text = " 吗？", });

                                        var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
                                        var lineText = this.editorBase.Document.GetText(line.Offset, line.Length);
                                        var pieces = Utils.Text.SplitToPiecesByRegex(@"!\[.*?\]\(.*?\)", lineText);
                                        Utils.TextPiece destPiece = null;
                                        var caretOffset = this.editorBase.SelectionStart - line.Offset;
                                        foreach (var piece in pieces)
                                        {
                                            if (piece.StartIndex < caretOffset && caretOffset < piece.EndIndex)
                                            {
                                                destPiece = piece;
                                                break;
                                            }
                                        }

                                        if (destPiece != null)
                                        {
                                            var answer = LMessageBox.Show(msgSpans, Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Question);
                                            if (answer != MessageBoxResult.OK) return "";

                                            this.editorBase.Select(line.Offset + destPiece.StartIndex, destPiece.Length);
                                        }

                                        if (dropList.Count == 1)//不然会出错。
                                        {
                                            this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortFileName + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, shortTitle.Length);
                                        }
                                        else
                                        {
                                            this.editorBase.SelectedText = "![" + shortTitle + "](./" + ShortResourceDirectoryPath + "/Images~/" + shortFileName + ")\r\n";
                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }
                                        break;
                                    }
                                case ResourceType.SoundFile:
                                    {
                                        var path = ShortResourceDirectoryPath + "/Sounds~/" + shortFileName;

                                        if (path.StartsWith("../") || path.StartsWith("./../"))
                                        {
                                            LMessageBox.ShowWarning("<embed>标签不支持跨越上层目录的相对路径。请注意此链接可以添加，但在网页中无效！");
                                        }

                                        if (shouldAddEntry)
                                            Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.SoundResourceDirectoryPath, destFilePath);

                                        if (dropList.Count == 1)
                                        {
                                            // this.editorBase.SelectedText = "<div class=\"sound\"><embed class=\"player\" src=\"./" + ShortResourceDirectoryPath + "/Sounds~/" + shortFileName + "\" autoplay =\"false\"/></div>";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + path + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, path.Length);
                                        }
                                        else
                                        {
                                            // 从操作逻辑上来看，这个要打开对应的工作区内资源文件夹才会发生。
                                            // this.editorBase.SelectedText = "<div class=\"sound\"><embed src=\"./" + ShortResourceDirectoryPath + "/Sounds~/" + shortFileName + "\" autoplay =\"false\"/></div>\r\n";

                                            //this.editorBase.Select(this.editorBase.SelectionStart + 12, ShortResourceDirectoryPath.Length + 11 + shortFileName.Length);
                                            //用上面这行会导致第二个链接被嵌入在第一个链接的 src="" 的引号中间。

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + path + ")";

                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }

                                        var leftText = this.editorBase.left_text;
                                        if (CustomMarkdownSupport.IsCodeBlockLine(leftText) == false && CustomMarkdownSupport.IsTreeListTextLine(leftText) == false)
                                        {
                                            LMessageBox.ShowWarning("请注意：音频、视频链接通常只应添加在树型文字表中用于演示。\r\n\r\n　　LME 由于需要兼容 CHM 和 Epub，对音视频的支持十分有限——在 Epub 中是无效的！");
                                        }
                                        break;
                                    }
                                case ResourceType.VedioFile:
                                    {
                                        var path = ShortResourceDirectoryPath + "/Sounds~/" + shortFileName;

                                        if (path.StartsWith("../") || path.StartsWith("./../"))
                                        {
                                            LMessageBox.ShowWarning("<embed>标签不支持跨越上层目录的相对路径。请注意此链接可以添加，但在网页中无效！");
                                        }

                                        if (shouldAddEntry)
                                            Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.VedioResourceDirectoryPath, destFilePath);

                                        if (dropList.Count == 1)
                                        {
                                            // this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Vedios~/" + shortFileName + "\" width =\"250\" height =\"180\"></embed>";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + path + ")";
                                            this.editorBase.Select(this.editorBase.SelectionStart + 2, path.Length);
                                        }
                                        else
                                        {
                                            // 从操作逻辑上来看，这个要打开对应的工作区内资源文件夹才会发生。
                                            // this.editorBase.SelectedText = "<embed src=\"./" + ShortResourceDirectoryPath + "/Vedios~/" + shortFileName + "\" width =\"250\" height =\"180\"></embed>\r\n";

                                            this.editorBase.SelectedText = "![" + shortTitle + "](" + path + ")";

                                            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                                        }

                                        var leftText = this.editorBase.left_text;
                                        if (CustomMarkdownSupport.IsCodeBlockLine(leftText) == false && CustomMarkdownSupport.IsTreeListTextLine(leftText) == false)
                                        {
                                            LMessageBox.ShowWarning("请注意：音频、视频链接通常只应添加在树型文字表中用于演示。\r\n\r\n　　LME 由于需要兼容 CHM 和 Epub，对音视频的支持十分有限——在 Epub 中是无效的！");
                                        }
                                        vedioFilesCount++;

                                        break;
                                    }
                            }
                        }
                    }

                    if (refreshWorkspace)
                    {
                        //在资源管理器界面进行刷新。
                        Globals.MainWindow.WorkspaceManager.RefreshWorkspaceTreeView(null);
                        //DONE: 已改掉，这个仅作将来备用，2017年1月21日。
                    }

                    if (ignoredFilesCount > 0)
                    {
                        LMessageBox.ShowWarning("下列文件路径中带空格，引用链接会出错。请先修改文件名（建议使用“_”字符代替空格）。\r\n\r\n" + sbIgnoredFiles.ToString());
                    }

                    //if (vedioFilesCount > 0)
                    //{
                    //    LMessageBox.Show("要插入兼容各版本浏览器的视频 Html 标签会导致文档很繁琐。故在想到好办法之前，不支持添加视频链接。",
                    //        Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                    //}
                    return string.Empty;
                }
                else
                {
                    return "没有资源文件可以拖放。";
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return ex.Message;
            }
        }

        private void InsertAbsoluteFileOrDirectoryPathLink(string fullPath)
        {
            if (File.Exists(fullPath) == false && Directory.Exists(fullPath) == false) return;

            var fileInfo = new FileInfo(fullPath);

            this.editorBase.SelectedText = "[" + fileInfo.Name + "](file:///" + fullPath + ")";
            this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
        }

        /// <summary>
        /// 创建引用的相对路径字符串前缀。
        /// </summary>
        /// <param name="layer">相对层级差。</param>
        private string BuildHtmlRefText(int layer)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < layer; i++)
            {
                sb.Append("../");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 查找当前插入点位置是否普通图像链接。
        /// </summary>
        /// <param name="isNormalMarkdownImageLink">是否普通 Markdown Iamge Link。随主题变化的复合链接会返回 false。</param>
        /// <returns>如果找到，返回路径；如果找不到，返回 null。</returns>
        public string FindExistLinkedImageFile(out bool isNormalMarkdownImageLink, out bool isComplexImageLink)
        {
            string relativePath;
            // string imageTitle = "";

            var line = this.editorBase.Document.GetLineByOffset(this.editorBase.SelectionStart);
            var lineText = this.editorBase.Document.GetText(line.Offset, line.EndOffset - line.Offset);

            var selIndex = this.editorBase.SelectionStart - line.Offset;

            int leftBracketIndex;
            int rightBracketIndex;
            if (this.editorBase.SelectionStart == line.Offset && this.editorBase.SelectionLength == line.Length)
            {
                leftBracketIndex = lineText.IndexOf("](", selIndex);
                rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                if (rightBracketIndex < 0)
                    rightBracketIndex = lineText.IndexOf(")", selIndex);
            }
            else
            {
                leftBracketIndex = lineText.LastIndexOf("](", selIndex);

                if (leftBracketIndex < 0)
                {
                    if (selIndex >= 1)
                    {
                        leftBracketIndex = lineText.LastIndexOf("![", selIndex - 1);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        leftBracketIndex = lineText.IndexOf("](", leftBracketIndex + 2);
                    }

                    if (leftBracketIndex >= 0)
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                    }
                    else
                    {
                        rightBracketIndex = lineText.IndexOf(" \"", selIndex);
                        if (rightBracketIndex < 0)
                            rightBracketIndex = lineText.IndexOf(")", selIndex);
                    }
                }
                else
                {
                    rightBracketIndex = lineText.IndexOf(" \"", leftBracketIndex + 1);
                    if (rightBracketIndex < 0)
                        rightBracketIndex = lineText.IndexOf(")", leftBracketIndex + 1);
                }
            }

            if (leftBracketIndex <= 0 || rightBracketIndex <= 1 || rightBracketIndex <= leftBracketIndex + 1)
            {
                isNormalMarkdownImageLink = false;
                isComplexImageLink = false;
                return null;
            }

            if (File.Exists(this.FullFilePath) == false)
            {
                isNormalMarkdownImageLink = false;
                isComplexImageLink = false;
                return null;
            }

            var startSquareBracketIndex = lineText.LastIndexOf("[", leftBracketIndex);

            relativePath = lineText.Substring(leftBracketIndex + 2, rightBracketIndex - leftBracketIndex - 2).Trim(new char[] { ' ', '　', '\t' });

            // 根据主题灵活变动的图像不支持替换 
            Regex regThemeImage = new Regex(@"\{%[^\/]{1,}%\}");
            var matchThemeImage = regThemeImage.Match(relativePath);
            if (matchThemeImage.Success)
            {
                isNormalMarkdownImageLink = false;
                isComplexImageLink = true;
                return null;
            }

            var pieces = relativePath.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (pieces.Length <= 0)
            {
                isNormalMarkdownImageLink = false;
                isComplexImageLink = false;
                return null;
            }

            FileInfo thisFileInfo = new FileInfo(this.FullFilePath);
            var directory = thisFileInfo.Directory;

            int x = 0;
            foreach (var s in pieces)
            {
                if (s == "..")
                {
                    directory = directory.Parent;
                    x++;
                }
                else
                {
                    var subPath = directory.FullName.EndsWith("\\") ? (directory.FullName + s) : (directory.FullName + "\\" + s);

                    if (Directory.Exists(subPath) == false)
                    {
                        if (WorkspaceTreeViewItem.IsValidateImageFilePath(subPath))
                        {
                            isNormalMarkdownImageLink = true;
                            isComplexImageLink = false;
                            //就是目标
                            return subPath;
                        }

                        isNormalMarkdownImageLink = false;
                        isComplexImageLink = false;
                        return null;
                    }
                    else
                    {
                        directory = new DirectoryInfo(subPath);
                        x++;
                    }
                }
            }

            if (x != pieces.Length - 1)
            {
                isNormalMarkdownImageLink = false;
                isComplexImageLink = false;
                return null;
            }

            isNormalMarkdownImageLink = false;
            isComplexImageLink = false;
            return null;
        }

        /// <summary>
        /// 尝试向当前编辑器粘贴图像数据块或音频文件（将来可能包括视频文件）。
        /// 粘贴时会要求用户给图像起个名称，然后快速保存到当前文档的图像资源文件夹中，
        /// 并自动生成对该文件的链接文本。
        /// * 注意：如果 Windows 系统的剪贴板被其它程序占用，会无法粘贴。
        /// </summary>
        public string TryToPasteResourceFileLink(bool callFromOtherFunction = false, string shortImageFileCommentName = null)
        {
            WinForm.IDataObject oDataObj = WinForm.Clipboard.GetDataObject();

            if (oDataObj != null && System.Windows.Clipboard.ContainsData(DataFormats.Bitmap))
            {
                try
                {
                    System.Drawing.Bitmap bitmap = null;
                    if (Clipboard.ContainsImage())
                    {
                        // ImageUIElement.Source = Clipboard.GetImage(); // does not work
                        System.Windows.Forms.IDataObject clipboardData = System.Windows.Forms.Clipboard.GetDataObject();
                        if (clipboardData != null)
                        {
                            if (clipboardData.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
                            {
                                bitmap = (System.Drawing.Bitmap)clipboardData.GetData(System.Windows.Forms.DataFormats.Bitmap);
                            }
                        }
                    }

                    if (bitmap == null) return "未能在剪贴板中提取图像数据块！";
                    var imageSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

                    System.Drawing.Image oImgObj = oDataObj.GetData(DataFormats.Bitmap, true) as System.Drawing.Image;
                    if (oImgObj == null) return "未能在剪贴板中提取图像数据块并转换为 Image 对象！";

                    //string shortImageFileCommentName;  // 改为参数


                    // 判断是否要直接替换图像
                    var imgFilePath = FindExistLinkedImageFile(out bool isNormalMarkdownImageLink, out bool isComplexImageLink);
                    if (isNormalMarkdownImageLink && File.Exists(imgFilePath) && isComplexImageLink == false)
                    {
                        var answer1 = LMessageBox.ShowQuestion($"当前插入点处有一个 Markdown 图像链接，指向{imgFilePath}。\r\n\r\n　　要替换此链接指向的图像文件吗？");
                        if (answer1 == MessageBoxResult.Yes)
                        {
                            var answer2 = LMessageBox.ShowQuestion($"此操作不可撤销，会影响所有引用此图像文件的链接。请再次确认，真的需要替换此链接指向的图像文件吗？");
                            if (answer2 == MessageBoxResult.Yes)
                            {
                                using (MemoryStream mStream = new MemoryStream())
                                {
                                    oImgObj.Save(mStream, System.Drawing.Imaging.ImageFormat.Png);
                                    oImgObj.Dispose();
                                    using (FileStream fs = new FileStream(imgFilePath, FileMode.Create))
                                    {
                                        mStream.WriteTo(fs);
                                    }
                                }

                                var imageTitle = PreviewLinkedImageFile(false, null, true);

                                //图像预览窗口则要看当前预览窗口是否正好显示同名的文件（因为现在允许同时打开多个预览窗口）。
                                foreach (var win in Globals.MainWindow.OwnedWindows)
                                {
                                    var ipw = win as ImagePreviewWindow;
                                    if (ipw == null) continue;
                                    if (imgFilePath.ToLower() == ipw.ImageFilePath.ToLower())
                                    {
                                        ipw.previewImage.Source = L.BuildBitmapImage(imgFilePath, out Size size);
                                        ipw.previewImage.Width = size.Width;
                                        ipw.previewImage.Height = size.Height;
                                        ipw.ImageTitle = imageTitle;
                                        //通常不需要这行，但须考虑“手动更改Markdown图像图像链接中的标题”的情况。
                                    }
                                }

                                WinForm.Clipboard.Clear();//否则下次复制、粘贴文本时总会弹出一个对话框要求设置要粘贴的图像的文件名。
                                return string.Empty;
                            } // 不同意替换
                        } // 不同意替换

                        LMessageBox.Show("请另选一个插入点位置，避开当前图像链接。");
                        return "";
                    } // 不是图像链接，直接继续

                    if (isComplexImageLink)
                    {
                        LMessageBox.Show("此处有一个复合图像链接，不适合粘贴。请另选一个插入点位置。");
                        return string.Empty;
                    }

                    if (Globals.MainWindow.PerspectiveMode == Perspective.MiniMode)
                    {
                        shortImageFileCommentName = "^";
                    }
                    else
                    {
                        if (string.IsNullOrWhiteSpace(shortImageFileCommentName))
                        {
                            var selText = this.editorBase.SelectedText.Trim().Replace("\r", "").Replace("\n", "");
                            if (string.IsNullOrWhiteSpace(selText))
                            {
                                selText = "";
                            }
                            else
                            {
                                if (selText.Length > 64)
                                {
                                    selText = selText.Substring(0, 64);
                                }
                            }
                            shortImageFileCommentName = InputBox.Show(Globals.AppName, "请输入图像文件名（不要后缀名，不要以“_”开头，尽量以字母开头）：", selText, true,
                                "注意：中文字符会被自动转换为拼音首字母。",
                                false, null, imageSource);
                        }
                    }

                    if (string.IsNullOrEmpty(shortImageFileCommentName)) return "用户未提供有效的文件名。";

                    var shortImageFileName = ChinesePinYin.ToChinesePinYinText(shortImageFileCommentName);

                    if (callFromOtherFunction == false)
                    {
                        if (Globals.MainWindow.AskEnglishFileName)
                        {
                            var regValidFileNameChars = new Regex(@"(\^|^[a-zA-Z0-9_][a-zA-Z0-9\-~_\.]{0,})");
                            if (regValidFileNameChars.IsMatch(shortImageFileCommentName) == false)
                            {
                                // 为什么要用英文文件名？
                                // 这是因为 CHM 对中文路径的支持有问题——很多网络设备对中文路径的支持也有问题。
                                shortImageFileName = InputBox.Show(Globals.AppName, $"您输入的【{shortImageFileCommentName}】将被用作图像标题。\r\n\r\n由于含有中文或特殊字符，建议使用下列字符串作文件名：",
                                    shortImageFileName, true, "说明：\r\n　　⑴建议的文件名是取中文拼音首字母生成的。\r\n　　⑵您可以在这里改成有意义的英文字符串。\r\n　　⑶空白字符会被替换为连字符。", false, true);
                                if (string.IsNullOrWhiteSpace(shortImageFileName)) return "用户未输入英文文件名";  // 用户放弃粘贴文件。
                            }
                        }

                        if (shortImageFileCommentName != "^")
                        {
                            shortImageFileName = ChinesePinYin.ToChinesePinYinText(shortImageFileCommentName.Replace(" ", "-").Replace("　", "-").Replace("\t", "-"));  // 防止用户仍然不慎输入了中文。
                        }
                    }

                    if (Directory.Exists(this.ImageResourceDirectoryPath) == false)
                    {
                        Directory.CreateDirectory(this.ImageResourceDirectoryPath);
                    }

                    string destFilePath;

                    if (shortImageFileCommentName == "^")
                    {
                        var now = DateTime.Now;
                        var shortName = $"{now.Year}{now.Month}{now.Day}-{now.Hour}{now.Minute}{now.Second}-{now.Millisecond}";
                        destFilePath = this.ImageResourceDirectoryPath + $"Img-{shortName}.png";
                    }
                    else
                    {
                        destFilePath = this.ImageResourceDirectoryPath + shortImageFileName + ".png";
                    }

                    if (callFromOtherFunction == false)
                    {
                        if (File.Exists(destFilePath))
                        {
                            var irdlg = new ImageReplaceDialog(destFilePath, shortImageFileCommentName)
                            {
                                Owner = Globals.MainWindow,
                            };
                            irdlg.imgNew.Source = imageSource;
                            //Console.WriteLine("Clipboard copied to UIElement");
                            irdlg.ShowDialog();

                            if (irdlg.DialogResult != true)
                            {
                                return "用户放弃操作！";
                            }

                            destFilePath = irdlg.ImgPath;
                            shortImageFileCommentName = irdlg.ShortImageFileCommentName;
                        }
                    }

                    //this.editorBase.SelectedText = "<img alt='" + shortImageFileName + "' width='" + oImgObj.Width + "px' height='" + oImgObj.Height +
                    //"px' src='./" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png' />";

                    //oImgObj.Save(destFilePath, System.Drawing.Imaging.ImageFormat.Png);
                    //用上面这行执行第二次粘贴同一个图像数据到同名文件时会出现异常。
                    using (MemoryStream mStream = new MemoryStream())
                    {
                        oImgObj.Save(mStream, System.Drawing.Imaging.ImageFormat.Png);
                        oImgObj.Dispose();
                        using (FileStream fs = new FileStream(destFilePath, FileMode.Create))
                        {
                            mStream.WriteTo(fs);
                        }
                    }

                    var destFileInfo = new FileInfo(destFilePath);
                    shortImageFileName = destFileInfo.Name.Substring(0, destFileInfo.Name.Length - destFileInfo.Extension.Length);
                    var commentTextFilePath = destFileInfo.Directory.FullName + "\\" + "~.txt";
                    if (File.Exists(commentTextFilePath))
                    {
                        using (var stream = File.AppendText(commentTextFilePath))
                        {
                            stream.WriteLine($"{shortImageFileName}{destFileInfo.Extension}|{shortImageFileCommentName}{destFileInfo.Extension}");
                        }
                    }
                    else
                    {
                        using (var stream = File.CreateText(commentTextFilePath))
                        {
                            stream.WriteLine($"{shortImageFileName}{destFileInfo.Extension}|{shortImageFileCommentName}{destFileInfo.Extension}");
                        }
                    }

                    if (callFromOtherFunction == false)
                    {
                        this.editorBase.SelectedText = "![" + shortImageFileCommentName + "](" + ShortResourceDirectoryPath + "/Images~/" + shortImageFileName + ".png)";

                        var selStart = this.editorBase.SelectionStart;
                        var selLine = this.editorBase.Document.GetLineByOffset(selStart);
                        this.editorBase.Select(selStart + this.editorBase.SelectionLength, 0);

                    }

                    WinForm.Clipboard.Clear();//否则下次复制、粘贴文本时总会弹出一个对话框要求设置要粘贴的图像的文件名。

                    //在资源管理器界面进行刷新。
                    //Globals.MainWindow.WorkspaceManager.Refresh(null);
                    //效率太低

                    Globals.MainWindow.InsertSubWorkspaceTreeViewItem(this.ImageResourceDirectoryPath, destFilePath);

                    if (callFromOtherFunction == false)
                    {
                        #region 刷新图像预览区域和可能存在的图像预览窗口中的图像。
                        //图像预览区域中直接显示当前粘贴的图像。
                        //Globals.MainWindow.ImagePreview.Source = L.BuildBitmapImage(destFileInfo.FullName);
                        var imageTitle = PreviewLinkedImageFile(false, null, true);

                        //图像预览窗口则要看当前预览窗口是否正好显示同名的文件（因为现在允许同时打开多个预览窗口）。
                        foreach (var win in Globals.MainWindow.OwnedWindows)
                        {
                            var ipw = win as ImagePreviewWindow;
                            if (ipw == null) continue;
                            if (destFilePath.ToLower() == ipw.ImageFilePath.ToLower())
                            {
                                ipw.previewImage.Source = L.BuildBitmapImage(destFilePath, out Size size);
                                ipw.previewImage.Width = size.Width;
                                ipw.previewImage.Height = size.Height;
                                ipw.ImageTitle = imageTitle;
                                //通常不需要这行，但须考虑“手动更改Markdown图像图像链接中的标题”的情况。
                            }
                        }
                        #endregion
                    }

                    // 自动加两行这个功能很是烦人
                    //if (selStart <= selLine.Offset)
                    //{
                    //    // ImageLink 单独占一行时，必须放在图像预览后面。
                    //    this.editorBase.SelectedText = "\r\n\r\n";
                    //    this.editorBase.Select(this.editorBase.SelectionStart + this.editorBase.SelectionLength, 0);
                    //}
                    //  //else // ImageLink 可能是行内图标

                    Globals.MainWindow.WorkspaceManager.SaveWorkspaceTreeviewToXml();

                    //var imgResourceDirectoryPath = this.ImageResourceDirectoryPath;
                    //if (imgResourceDirectoryPath.EndsWith("\\"))
                    //    imgResourceDirectoryPath = imgResourceDirectoryPath.Substring(0, imgResourceDirectoryPath.Length - 1).ToLower();

                    //var imgResourceItem = Globals.MainWindow.FindWorkspaceTreeViewItem(imgResourceDirectoryPath);
                    //imgResourceItem.Items.Add(new WorkspaceTreeViewItem(destFilePath, Globals.MainWindow));
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
            else
            {
                return TryToDropResourcesFiles(Clipboard.GetFileDropList(), false);
            }

            return string.Empty;
        }


        /// <summary>
        /// 此方法仅适用于纯色背景的情况。
        /// 方法修改自：<https://www.codeproject.com/Questions/1090410/How-to-make-PNG-image-transparent>
        /// </summary>
        /// <param name="pathOfPngFile">png 文件的完整路径。</param>
        /// <paramref name="transColor">默认情况下应传入 System.Drawing.Color.Transparent，适用于去除白背景。</paramref>
        public static bool MakePngFileTransparent(string pathOfPngFile, System.Drawing.Color transColor)
        {
            byte[] newImage = new byte[0];
            var error = string.Empty;
            using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(pathOfPngFile))
            {
                System.Drawing.Color pixel = bmp.GetPixel(0, 0);
                if (pixel.A != 0)
                {
                    // Make backColor transparent for myBitmap.
                    //transColor = System.Drawing.Color.FromArgb(docBackBrush.Color.A,
                    //                                     docBackBrush.Color.R,
                    //                                     docBackBrush.Color.G,
                    //                                     docBackBrush.Color.B);
                    bmp.MakeTransparent(transColor);

                    System.Drawing.ImageConverter converter = new System.Drawing.ImageConverter();
                    newImage = (byte[])converter.ConvertTo(bmp, typeof(byte[]));
                    bmp.Dispose();
                }
                else
                {
                    FileStream fs = new FileStream(pathOfPngFile, FileMode.OpenOrCreate, FileAccess.Read);
                    newImage = new byte[fs.Length];
                    fs.Read(newImage, 0, System.Convert.ToInt32(fs.Length));
                    fs.Close();
                }
            }

            try
            {
                File.Delete(pathOfPngFile);
                using (FileStream fs = new FileStream(pathOfPngFile, FileMode.Create))
                {
                    fs.Write(newImage, 0, newImage.Length);
                }
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的资源文件夹的短名称。
        /// </summary>
        public string ShortResourceDirectoryPath
        {
            get
            {
                var shortFileName = this.ShortFileName;      //.ToLower();  加上这个会导致粘贴的图像在 Linux 服务器中无法显示。
                if (string.IsNullOrEmpty(shortFileName)) return string.Empty;

                if (shortFileName.EndsWith(".md")) return shortFileName.Substring(0, shortFileName.Length - 3) + "~";
                return string.Empty;
            }
        }

        public string 资源文件夹短名
        {
            get { return ShortResourceDirectoryPath; }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的资源文件夹的完整路径。
        /// </summary>
        public string ResourceDirectoryPath
        {
            get
            {
                if (File.Exists(this.FullFilePath) == false) return string.Empty;
                return this.FullFilePath.Substring(0, this.fullFilePath.Length - 3) + "~\\";
            }
        }

        public string 资源文件夹路径
        {
            get { return ResourceDirectoryPath; }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的图像资源文件夹的完整路径。
        /// </summary>
        public string ImageResourceDirectoryPath
        {
            get
            {
                var resourceDirectoryPath = this.ResourceDirectoryPath;
                if (string.IsNullOrEmpty(resourceDirectoryPath))
                {
                    return string.Empty;
                }

                return resourceDirectoryPath + "Images~\\";
            }
        }

        public string 图像资源文件夹路径
        {
            get { return ImageResourceDirectoryPath; }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的声音资源文件夹的完整路径。
        /// </summary>
        public string SoundResourceDirectoryPath
        {
            get
            {
                var resourceDirectoryPath = this.ResourceDirectoryPath;
                if (string.IsNullOrEmpty(resourceDirectoryPath))
                {
                    return string.Empty;
                }

                return resourceDirectoryPath + "Sounds~\\";
            }
        }

        public string 声音资源文件夹路径
        {
            get { return SoundResourceDirectoryPath; }
        }

        /// <summary>
        /// 当前 Markdown 文件对应的视频资源文件夹的完整路径。
        /// </summary>
        public string VedioResourceDirectoryPath
        {
            get
            {
                var resourceDirectoryPath = this.ResourceDirectoryPath;
                if (string.IsNullOrEmpty(resourceDirectoryPath))
                {
                    return string.Empty;
                }

                return resourceDirectoryPath + "Vedios~\\";
            }
        }

        public string 视频资源文件夹路径
        {
            get { return VedioResourceDirectoryPath; }
        }

        /// <summary>
        /// 文档被修改状态变化时，更改编辑器标头状态字符。
        /// 这个方案有时候并不准确。换成绑定IModified这个依赖项属性才是正解。
        /// </summary>
        //void UndoStack_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        //{
        //    //用于判断文档是否已被修改。
        //    if (e.PropertyName == "IsOriginalFile")
        //    {
        //        modifiedTextBlock.Text = editorBase.Document.UndoStack.IsOriginalFile ? string.Empty : "*";
        //    }
        //}

        private string htmlDocumentPageHeader = string.Empty;
        /// <summary>
        /// Html 页面标题文本
        /// </summary>
        public string HtmlDocumentPageHeader
        {
            get
            {
                if (string.IsNullOrEmpty(this.htmlDocumentPageHeader))
                {
                    return string.Empty;
                }
                return this.htmlDocumentPageHeader;
            }
        }

        private string htmlDocumentTitle = string.Empty;
        /// <summary>
        /// 编译后的html文档的标题，在MarkDown文档中以%（或%）顶格开头。
        /// </summary>
        public string HtmlDocumentTitle
        {
            get
            {
                //if (string.IsNullOrEmpty(this.editorBase.Text)) return string.Empty;

                //string[] lines = this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                //foreach (var line in lines)
                //{
                //    char[] trimStartChars = new char[] { '\t', ' ', '　' };
                //    var s = line.TrimStart(trimStartChars);

                //    if (s.StartsWith("%") || s.StartsWith("%"))
                //    {
                //        if (s.Length >= 2)
                //        {
                //            if (s[1] != '%' && s[1] != '%')
                //                return s.Substring(1).Replace("%", "").Replace("%", "").Replace("\t", "").TrimEnd(new char[] { '\t', ' ', '　' });
                //        }
                //    }
                //}
                //return string.Empty;
                if (string.IsNullOrEmpty(this.htmlDocumentTitle))
                {
                    return "Markdown Document";
                }
                return this.htmlDocumentTitle;
            }
        }

        private string footerText = string.Empty;
        /// <summary>
        /// 编译后的html文本的脚注文本，在MarkDown文档档中以@(或·）顶格开头。
        /// </summary>
        public string FooterText
        {
            get { return footerText; }
        }

        private bool isExamEnabled = false;
        /// <summary>
        /// 是否启用试题编辑功能。（依赖“自动完成”功能）
        /// </summary>
        public bool IsExamEnabled
        {
            get
            {
                return isExamEnabled;
            }

            set
            {
                isExamEnabled = value;
                this.editorBase.IsExamEnabled = isExamEnabled;
            }
        }

        private bool isAutoCompletionEnabled = false;
        /// <summary>
        /// 是否启用“自动完成”功能。
        /// </summary>
        public bool IsAutoCompletionEnabled
        {
            get { return isAutoCompletionEnabled; }
            set
            {
                isAutoCompletionEnabled = value;
                this.editorBase.IsAutoCompletionEnabled = value;
            }
        }

        private bool isEnToChineseDictEnabled = false;
        /// <summary>
        /// 是否启用英译中词条自动提示功能。（依赖“自动完成”功能）
        /// </summary>
        public bool IsEnToChineseDictEnabled
        {
            get { return isEnToChineseDictEnabled; }
            set
            {
                isEnToChineseDictEnabled = value;
                this.editorBase.IsEnToChineseDictEnabled = value;
            }
        }

        private MarkdownEditorBase.HighlightingType highlightingSetting = MarkdownEditorBase.HighlightingType.Advance;
        /// <summary>
        /// 采用何种高亮显示方案。
        /// </summary>
        public MarkdownEditorBase.HighlightingType HighlightingSetting
        {
            get { return highlightingSetting; }
            set
            {
                highlightingSetting = value;
                this.editorBase.HighlightingSetting = value;
            }
        }

        private bool isShowSpaces = false;
        /// <summary>
        /// 决定是否显示空格。
        /// </summary>
        public bool IsShowSpaces
        {
            get { return isShowSpaces; }
            set
            {
                isShowSpaces = value;
                this.editorBase.IsShowSpaces = value;
            }
        }

        private bool isShowEndOfLine = false;
        /// <summary>
        /// 决定是否显示段落标记。
        /// </summary>
        public bool IsShowEndOfLine
        {
            get { return isShowEndOfLine; }
            set
            {
                isShowEndOfLine = value;
                this.editorBase.IsShowEndOfLine = value;
            }
        }

        private bool isShowTabs = false;
        /// <summary>
        /// 决定是否显示空格。
        /// </summary>
        public bool IsShowTabs
        {
            get { return isShowTabs; }
            set
            {
                isShowTabs = value;
                this.editorBase.IsShowTabs = value;
            }
        }


        private bool isShowLineNumbers = true;
        /// <summary>
        /// 决定是否显示行号。
        /// </summary>
        public bool IsShowLineNumbers
        {
            get { return isShowLineNumbers; }
            set
            {
                isShowLineNumbers = value;
                this.editorBase.ShowLineNumbers = value;
            }
        }

        private bool isTextAutoWrap = false;
        /// <summary>
        /// 决定是否显示空格。
        /// </summary>
        public bool IsTextAutoWrap
        {
            get { return isTextAutoWrap; }
            set
            {
                isTextAutoWrap = value;
                this.editorBase.WordWrap = value;
            }
        }

        /// <summary>
        /// 编译当前编辑器中的 Markdown 文本并预览。
        /// </summary>
        /// <param name="callSystemDefaultWebBrowser">是否调用 Windows 系统默认浏览器预览。</param>
        public string CompileAndPreviewHtml(bool callSystemDefaultWebBrowser = false)
        {
            if (File.Exists(this.fullFilePath) == false || this.editorBase.IsModified)
            {
                var result = LMessageBox.Show("文件需要先保存才可以预览！要保存吗？", Globals.AppName,
                    MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
                if (result != MessageBoxResult.OK)
                {
                    this.editorBase.Focus();
                    return null;
                }
                else
                {
                    var saveresult = this.SaveDocument();
                    if (saveresult != string.Empty)
                    {
                        LMessageBox.Show(saveresult, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }
                    else
                    {
                        AnchorsManager.Load();
                    }
                }
            }

            if (this.fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
            {
                LMessageBox.Show("只有在默认工作区目录下的文件才可以预览！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                this.editorBase.Focus();
                return null;
            }

            string newFilePath = Compile();

            if (File.Exists(newFilePath))
            {
                //this.webBrowser.NavigateToString(htmlDocumentText);
                //WinForm内置的WebBrowser总是出现“JSON未定义错误”，而现代浏览器则不会，所以直接调用外部浏览器好了。

                //System.Diagnostics.Process.Start(newFilePath);

                var destUri = new Uri(newFilePath);//不能加这个前缀

                if (callSystemDefaultWebBrowser == false)
                {
                    if (Globals.MainWindow.previewFrame.Source == null ||
                        Globals.MainWindow.previewFrame.Source.AbsolutePath != destUri.AbsolutePath)
                    {
                        Globals.MainWindow.previewFrame.Source = destUri;
                    }
                    else Globals.MainWindow.previewFrame.Refresh();

                    Globals.MainWindow.tcRightToolBar.SelectedItem = Globals.MainWindow.tiHtmlPreview;

                    if (Globals.MainWindow.PerspectiveMode != Perspective.VerticalMode)
                    {
                        if (Globals.MainWindow.tcRightToolBar.ActualWidth < 100)
                        {
                            Globals.MainWindow.cdMainEditArea.Width =
                                Globals.MainWindow.cdRightToolsArea.Width = new GridLength(3, GridUnitType.Star);
                            //Globals.MainWindow.cdLeftToolsArea.Width = new GridLength(460, GridUnitType.Pixel);
                        }
                    }
                    else
                    {
                        if (Globals.MainWindow.rdTrdPreviewArea.ActualHeight < 140)
                        {
                            Globals.MainWindow.rdTrdPreviewArea.Height = new GridLength(
                                (Globals.MainWindow.gridMainEdiorArea.ActualHeight - Globals.MainWindow.bdInputedCommandText.ActualHeight - Globals.MainWindow.btnCollapseFindAndReplacePanel.ActualHeight) / 2, GridUnitType.Pixel);
                        }
                    }

                    this.editorBase.Focus();
                }
                else
                {
                    System.Diagnostics.Process.Start("explorer.exe", $"\"{newFilePath}\"");
                    //不能用destUri.AbsoluteUri，因为会进行URL转码导致找不到文件;
                    //不能加这个前缀"file:///" + 
                }

                return newFilePath;
            }

            return null;
        }

        public string 编译并预览(bool callSystemDefaultWebBrowser = false)
        {
            return CompileAndPreviewHtml(callSystemDefaultWebBrowser);
        }

        public string Compile()
        {
            string htmlTitle = "";

            string newFilePath = CustomMarkdownSupport.CompileOrGetHtmlFile(
                this.fullFilePath,  // 要编译的文本路径
                Globals.MainWindow.HtmlHeadersCollapse,  // 标题折叠状态
                null,  // 编译为演示用 Html 文件时选用的切分方式 
                1,  // 第一个 H1 级标题的起始编号
                ref htmlTitle,  // 输出 Html 标题
                false  // 添加“回目录”链接
                       // 不带装饰器
                       // 如果已存在目标 Html 文件，直接返回其路径，不进行编译
                );
            return newFilePath;
        }

        /// <summary>
        /// 按照一定的标准切分Markdown文档为一个或多个Markdonw片段，
        /// 再编译当前编辑器中的 Markdown 文本为一个（或多个）Html文件并预览。
        /// 注意：如果 TextSegment 非空，则只显示一页。
        /// </summary>
        public void CompileAndPresentateHtml(
            CustomMarkdownSupport.PresentateHtmlSplitterType presentateHtmlSplitterType,
            bool onlyPresentateHeaders = false, string textSegment = null)
        {
            if (File.Exists(this.fullFilePath) == false || this.editorBase.IsModified)
            {
                var result = LMessageBox.Show("文件需要先保存才可以预览！要保存吗？", Globals.AppName,
                    MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);
                if (result != MessageBoxResult.OK)
                {
                    this.editorBase.Focus();
                    return;
                }
                else
                {
                    var saveresult = this.SaveDocument();
                    if (saveresult != string.Empty)
                    {
                        LMessageBox.Show(saveresult, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        return;
                    }
                    else
                    {
                        AnchorsManager.Load();
                    }
                }
            }

            if (this.fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
            {
                LMessageBox.Show("只有在默认工作区目录下的文件才可以演示！",
                    Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
                this.editorBase.Focus();
                return;
            }

            List<string> htmlFilesList = new List<string>();
            List<string> mdFilesList = new List<string>();

            CustomMarkdownSupport.PresentateHtmlSplitterType realPresentateHtmlSplitterType;

            if (presentateHtmlSplitterType == CustomMarkdownSupport.PresentateHtmlSplitterType.ByDocument)
            {
                realPresentateHtmlSplitterType = this.PresentateHtmlSplitterType;
            }
            else
            {
                realPresentateHtmlSplitterType = presentateHtmlSplitterType;
            }

            var urisList = CustomMarkdownSupport.Compile(this.fullFilePath,
                    Globals.MainWindow.HtmlHeadersCollapse, realPresentateHtmlSplitterType,
                    ref htmlFilesList, ref mdFilesList, onlyPresentateHeaders, textSegment);

            if (urisList != null && urisList.Count > 0)
            {
                if (string.IsNullOrEmpty(textSegment))
                {
                    //单独弹出一个窗口来演示。
                    var pw = new PresentationHtmlWindow(this, htmlFilesList, mdFilesList)
                    {
                        //Owner = App.Current.MainWindow,
                    };
                    pw.UrisList = urisList;
                    //pw.Title = this.headerTextBlock.Text;
                    pw.Show();
                }
                else
                {
                    //在主窗口左工具栏顶部的“Html片段预览区域”预览。
                    if (presentateHtmlSplitterType == CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment &&
                        urisList.Count >= 1)
                    {
                        if (Globals.MainWindow.segmentPreviewWebBrowser.Source == null ||
                            Globals.MainWindow.segmentPreviewWebBrowser.Source.LocalPath != urisList[0].Uri.LocalPath)
                        {
                            Globals.MainWindow.segmentPreviewWebBrowser.Source = urisList[0].Uri;
                        }
                        else
                        {
                            Globals.MainWindow.segmentPreviewWebBrowser.Refresh();
                        }

                        if (Globals.MainWindow.tcManagerPanels.SelectedItem != Globals.MainWindow.tiHtmlSegmentPreview)
                        {
                            Globals.MainWindow.tcManagerPanels.SelectedItem = Globals.MainWindow.tiHtmlSegmentPreview;
                        }

                        //设置片段预览区的高度，移动到MainWindow.xaml.cs中的segmentPreviewWebBrowser_LoadCompleted事件处理方法中
                        //Globals.MainWindow.rdResourcePreviewArea.Height = new GridLength(0);
                        //Globals.MainWindow.rdLeftToolsTop.Height = new GridLength(1, GridUnitType.Star);
                    }
                }
            }
            else
            {
                switch (realPresentateHtmlSplitterType)
                {
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.Region:
                        LMessageBox.Show("未找到可以演示的自定义折叠区。", Globals.AppName,
                             MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader:
                        LMessageBox.Show("未找到可以演示的一级标题。", Globals.AppName,
                             MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.HorizontalLine:
                        LMessageBox.Show("未找到可以演示的水平线切分区域。", Globals.AppName,
                             MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.ByDocument:
                        LMessageBox.Show("文档中没有可供演示的有效内容。", Globals.AppName,
                             MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.WholeDocument:
                        LMessageBox.Show("文档中没有可供演示的有效内容。", Globals.AppName,
                            MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                    case CustomMarkdownSupport.PresentateHtmlSplitterType.TextSegment:
                        LMessageBox.Show("未提供可供演示的文本片段。\r\n　　请注意：三种注释中只有一种属于真正可以演示的内容。",
                            Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                        break;
                }
            }
        }

        /// <summary>
        /// 取当前文档中定义的演示模式。如果没有定义，返回“按一级标题演示”。
        /// </summary>
        public CustomMarkdownSupport.PresentateHtmlSplitterType PresentateHtmlSplitterType
        {
            get
            {
                var lines = this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

                Regex regex = new Regex(@"^[   ]{0,3}[;；](([pPｐＰ][mMｍＭ])|(演示(模式)?))[:：]");
                foreach (var line in lines)
                {
                    var match = regex.Match(line);
                    if (match != null && match.Success)
                    {
                        var tail = line.Substring(match.Length).Trim(new char[] { ' ', '　', '\t' });
                        if (new Regex(@"^[#＃].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
                        if (new Regex(@"^[-－].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.HorizontalLine;
                        if (new Regex(@"^[rRｒＲ\{\}｛｝].*$").Match(tail).Success) return CustomMarkdownSupport.PresentateHtmlSplitterType.Region;
                        return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
                    }
                }

                return CustomMarkdownSupport.PresentateHtmlSplitterType.TopLevelHeader;
            }
        }

        /// <summary>
        /// 关闭编辑器。
        /// </summary>
        void closeImage_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// 关闭编辑器。
        /// </summary>
        private void Close()
        {
            if (this.IsModified)
            {
                string documentTitleAndFileName = null;
                foreach (var line in this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
                {
                    documentTitleAndFileName = CustomMarkdownSupport.GetDocumentTitle(line);
                    if (string.IsNullOrWhiteSpace(documentTitleAndFileName) == false) break;
                }

                if (string.IsNullOrWhiteSpace(documentTitleAndFileName))
                {
                    documentTitleAndFileName = Path.GetFileName(this.FullFilePath);
                }
                else
                {
                    documentTitleAndFileName += "｜" + Path.GetFileName(this.FullFilePath);
                }

                if (string.IsNullOrWhiteSpace(documentTitleAndFileName) == false)
                    documentTitleAndFileName = "【" + documentTitleAndFileName + "】";

                MessageBoxResult r = LMessageBox.Show($"文档{documentTitleAndFileName}尚未保存，要先保存文件吗？",
                    Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Exclamation);
                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            //保存
                            var saveResult = SaveDocument();
                            if (string.Empty != saveResult)
                            {
                                LMessageBox.Show(saveResult, Globals.AppName, MessageBoxButton.OKCancel, MessageBoxImage.Error);
                                return;
                            }
                            else
                            {
                                AnchorsManager.Load();
                            }
                            break;
                        }
                    case MessageBoxResult.No:
                        {
                            //不保存
                            break;
                        }
                    case MessageBoxResult.Cancel: return;
                }
            }

            //关闭文档
            BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
            if (parentTabControl == null) return;

            if (parentTabControl.Items.Contains(this))
            {
                parentTabControl.Items.Remove(this);
            }
        }

        /// <summary>
        /// [静态构造方法]
        /// </summary>
        static MarkdownEditor()
        {
            closeImageSource = new BitmapImage(new Uri("pack://application:,,,/LunarMarkdownEditor;component/Images/CloseHS.png"));
            closeBorderMargin = new Thickness(4, 0, 0, 0);

            singleTagsList.Add("br");
            singleTagsList.Add("hr");
            singleTagsList.Add("img");
            singleTagsList.Add("input");
            singleTagsList.Add("param");
            singleTagsList.Add("meta");
            singleTagsList.Add("link");
        }

        private static Thickness closeBorderMargin;

        private static BitmapImage closeImageSource;

        private StackPanel headerPanel = new StackPanel() { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 2, 0, 2), };
        /// <summary>
        /// 标头上用来显示文件短名的文本块。
        /// </summary>
        private TextBlock headerTextBlock = new TextBlock()
        {
            FontSize = 14,
            VerticalAlignment = VerticalAlignment.Center,
            FontFamily = new FontFamily("Arial,Simsun"),
            Margin = new Thickness(0, 2, 0, 2),
        };

        /// <summary>
        /// 标头上用以显示文件是否被修改的状态的文本块。
        /// </summary>
        private TextBlock modifiedTextBlock = new TextBlock() { FontSize = 16, };

        private TextBlock readonlyTextBlock = new TextBlock()
        {
            FontSize = 14,
            FontFamily = new FontFamily("Simsun"),
            Foreground = Brushes.Red,
            Text = "[只读]",
            ToolTip = "鼠标左键点击退出只读状态。",
            Margin = new Thickness(2, 0, 2, 0),
            VerticalAlignment = VerticalAlignment.Center,
        };

        private Image closeImage = new Image() { Width = 16, Height = 24, };

        private Border closeBorder = new Border() { Margin = closeBorderMargin, ToolTip = "关闭[Ctrl+W | Ctrl+F4]", };

        /// <summary>
        /// 关闭文档。
        /// </summary>
        void miCloseDocument_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            if (this.IsModified)
            {
                MessageBoxResult r = LMessageBox.Show("当前文档已修改，要保存吗？", Globals.AppName, MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);
                switch (r)
                {
                    case MessageBoxResult.Yes:
                        {
                            string saveInfo = SaveDocument();
                            if (saveInfo == string.Empty)
                            {
                                BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                                if (parentTabControl != null)
                                {
                                    if (parentTabControl.Items.Contains(this))
                                    {
                                        parentTabControl.Items.Remove(this);
                                    }
                                }

                                AnchorsManager.Load();
                            }
                            else if (saveInfo == "用户取消操作")
                            {
                                return;//不关闭。
                            }
                            else
                            {
                                LMessageBox.Show("未能保存文件！错误消息如下：\r\n" + saveInfo);
                            }
                            return;
                        }
                    case MessageBoxResult.No:
                        {
                            BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                            if (parentTabControl != null)
                            {
                                if (parentTabControl.Items.Contains(this))
                                {
                                    parentTabControl.Items.Remove(this);
                                }
                            }
                            return;
                        }
                    default://Cancel
                        {
                            return;
                        }
                }
            }
            else
            {
                BetterWpfControls.TabControl parentTabControl = this.Parent as BetterWpfControls.TabControl;
                if (parentTabControl != null)
                {
                    if (parentTabControl.Items.Contains(this))
                    {
                        parentTabControl.Items.Remove(this);
                    }
                }
            }
        }

        /// <summary>
        /// 格式化 Markdown 文本。
        /// </summary>
        public string FormatedMarkdownText()
        {
            return CustomMarkdownSupport.FormatMarkdownText(this.editorBase.Text);
        }

        /// <summary>
        /// 格式化二维文字表。
        /// </summary>
        public void FormatTextTable(bool autoAggregate = false)
        {
            this.editorBase.FormatTextTable(autoAggregate);
        }

        /// <summary>
        /// 保存文档。顺利完成就返回 string.Empth。否则返回错误消息。
        /// </summary>
        public string SaveDocument()
        {
            if (string.IsNullOrEmpty(this.fullFilePath))
            {
                return SaveDocumentAs();
            }

            try
            {
                var fstLine = EncryptedFirstLine;
                if (string.IsNullOrEmpty(fstLine))
                {
                    var text = Utils.Text.FormatLineSplitter(this.editorBase.Text);

                    //this.editorBase.Save(this.fullFilePath);//如果没有密码，则不加密。
                    File.WriteAllText(this.fullFilePath, text, Encoding.UTF8);
                }
                else
                {
                    var text = Utils.Text.FormatLineSplitter(this.editorBase.Document.Text);
                    File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(text, "DyBj#PpBb"))}");

                }

                this.editorBase.IsModified = false;//设置保存状态。

                RefreshEditingStatu();

                OnSaved();
                return string.Empty;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        public string 保存()
        {
            return SaveDocument();
        }

        private void SaveAnchorSpans()
        {
            try
            {
                var reg = new Regex(@"\[.*\][\(（]@.*[\)）]");
                var reg2 = new Regex(@"(?<=[\(（][ 　\t]*?[@][ 　\t]*?)[a-zA-Z][a-zA-Z0-9\-_]*(?=[ 　\t]*?[\)）])");  // 简写版的格式是： (@id)

                StringBuilder sb = new StringBuilder();
                var rPath = this.RelativePathToWorkspace;
                var srcText = this.editorBase.Text.Replace("\r\n", "\n");
                srcText = srcText.Replace("\r", "\n");

                string[] lines = this.editorBase.Text.Split(new char[] { '\n', }, StringSplitOptions.None);

                var tailPath = this.FullFilePath;
                if (tailPath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()))
                {
                    tailPath = tailPath.Substring(Globals.PathOfWorkspace.Length);
                }

                AnchorWrap aw = new AnchorWrap(tailPath);

                foreach (var line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line) || CustomMarkdownSupport.IsCodeBlockLine(line)) continue;
                    // 代码块中的锚不应起作用

                    var matches = reg.Matches(line);
                    var matches2 = reg2.Matches(line);

                    if (matches.Count > 0 || matches2.Count > 0)
                    {
                        sb.Append(rPath);
                        sb.Append("\r\n");
                    }

                    if (matches.Count > 0)
                    {
                        foreach (var match in matches)
                        {
                            var m = match as Match;
                            if (m == null) continue;

                            sb.Append("\t" + m.Value + "\r\n");
                            aw.Add(new Anchor(aw, m.Value));
                        }
                    }

                    if (matches2.Count > 0)
                    {
                        foreach (var match in matches2)
                        {
                            var m = match as Match;
                            if (m == null) continue;

                            sb.Append("\t[](@" + m.Value + ")\r\n");
                            aw.Add(new Anchor(aw, m.Value));
                        }
                    }
                }

                if (aw.Anchors.Count > 0)
                {
                    AnchorsManager.Add(aw);
                }

                sb.Append("\r\n");  //空行用以分隔两个不同的文件中的锚片段。

                if (File.Exists(Globals.PathOfWorkspaceAnchorsFilePath))
                {
                    using (var sw = new StreamWriter(Globals.PathOfWorkspaceAnchorsFilePath, true, Encoding.UTF8))
                    {
                        sw.Write(FormatAutoNumberOfAnchors(sb.ToString()));
                    }
                }
                else
                {
                    using (var fs = new FileStream(Globals.PathOfWorkspaceAnchorsFilePath, FileMode.OpenOrCreate))
                    {
                        using (var sw = new StreamWriter(fs, Encoding.UTF8))
                        {
                            sw.Write(sb.ToString());
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LMessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        public static string FormatAutoNumberOfAnchors(string sourceText)
        {
            var lines = sourceText.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            var sb = new StringBuilder();

            var reg = new Regex(@"\[[ 　\t]{0,}?\^[ 　\t]{0,}?\](?=(\]\([@]))");

            int number = 1;

            foreach (var line in lines)
            {
                var lineText = line;
                var matches = reg.Matches(line);
                if (matches.Count > 0)
                {
                    number += (matches.Count - 1);
                    for (int i = matches.Count - 1; i >= 0; i--)
                    {
                        var match = matches[i];
                        lineText = lineText.Substring(0, match.Index) + "[" + number.ToString() + "]" + lineText.Substring(match.Index + match.Length);
                        number--;
                    }
                    number += matches.Count;
                    number++;
                }

                sb.Append(lineText);
                sb.Append("\r\n");
            }

            return sb.ToString();
        }

        /// <summary>
        /// 当前文件相对于工作区的路径。例：
        /// 工作区目录：D:\示例工作区\LWS-MyWorkspace
        /// 当前文件目录：D:\示例工作区\LWS-MyWorkspace\Unit-01\Doc-01.md
        /// 则此路径值为：unit-01\doc-01.md   （全小写）
        /// </summary>
        public string RelativePathToWorkspace
        {
            get
            {
                var fullPath = this.FullFilePath.ToLower();
                var workspacePath = Globals.PathOfWorkspace.ToLower();
                if (fullPath.StartsWith(workspacePath))
                {
                    return fullFilePath.Substring(workspacePath.Length);
                }

                return "";
            }
        }

        public string 在工作区中的相对路径
        {
            get { return RelativePathToWorkspace; }
        }

        /// <summary>
        /// 文档另存为。顺利完成就返回 string.Empth。否则返回错误消息。
        /// </summary>
        public string SaveDocumentAs()
        {
            try
            {
                SaveFileDialog sfd = new SaveFileDialog() { Title = Globals.AppName, };
                sfd.Filter = Globals.DocumentName +
                "(*." + Globals.ExtensionName + ")|*." + Globals.ExtensionName;
                sfd.FilterIndex = 0;
                sfd.FileName = this.shortFileName;
                sfd.InitialDirectory = Globals.PathOfWorkspace;

                if (sfd.ShowDialog() == true)
                {
                    if (sfd.FileName.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false)
                    {
                        var reslut = LMessageBox.Show("只有保存在默认工作区目录下才可以预览！真的要继续吗？", Globals.AppName,
                            MessageBoxButton.OKCancel, MessageBoxImage.Exclamation);

                        if (reslut != MessageBoxResult.OK) return "用户放弃保存。";
                    }

                    var fstLine = EncryptedFirstLine;
                    if (string.IsNullOrEmpty(fstLine))
                    {
                        var text = Utils.Text.FormatLineSplitter(this.editorBase.Text);
                        // this.editorBase.Save(this.fullFilePath);//要考虑加密，不能直接用这个
                        File.WriteAllText(this.fullFilePath, text, Encoding.UTF8);
                    }
                    else
                    {
                        var text = Utils.Text.FormatLineSplitter(this.editorBase.Document.Text);
                        File.WriteAllText(this.FullFilePath, $"{fstLine}\r\n{new String(SetPasswordPanel.TextEncrypt(text, "DyBj#PpBb"))}");
                    }

                    this.FullFilePath = sfd.FileName;//更新文件路径。
                    this.editorBase.IsModified = false;//设置保存状态。
                    RefreshEditingStatu();

                    OnSaved();
                    return string.Empty;
                }
                else
                {
                    return "用户放弃保存";
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 载入文件后，或文档后更新文档编辑状态到标头位置。
        /// </summary>
        private void RefreshEditingStatu()
        {
            var trimChars = new char[] { ' ', '　', '\t', };
            var fstTextLine = this.editorBase.Document.GetLineByNumber(1);
            if (fstTextLine != null)
            {
                var fstLineText = this.editorBase.Document.GetText(fstTextLine.Offset, fstTextLine.Length);
                if (string.IsNullOrWhiteSpace(fstLineText))
                {
                    // 默认状态
                    this.iconBorder.BorderBrush = Brushes.Red;
                    this.iconBorder.ToolTip = this.icon.ToolTip = "未开始";
                }
                else
                {
                    var finishHeader = CustomMarkdownSupport.GetHeaderOfTaskListItem(fstLineText);
                    var finishContent = CustomMarkdownSupport.GetContentOfTaskListItem(fstLineText).Trim(trimChars);
                    switch (finishHeader.Trim(trimChars))
                    {
                        case "[%]":
                            this.iconBorder.BorderBrush = Brushes.Green;
                            if (string.IsNullOrEmpty(finishContent) == false)
                                this.iconBorder.ToolTip = this.icon.ToolTip = "进行中 ◆ " + finishContent;
                            else this.iconBorder.ToolTip = this.icon.ToolTip = "进行中";
                            break;
                        case "[+]":
                            this.iconBorder.BorderBrush = Brushes.Blue;
                            if (string.IsNullOrEmpty(finishContent) == false)
                                this.iconBorder.ToolTip = this.icon.ToolTip = "已完成 ◆ " + finishContent;
                            else this.iconBorder.ToolTip = this.icon.ToolTip = "已完成";
                            break;
                        case "[#]":
                            this.iconBorder.BorderBrush = Brushes.Brown;
                            if (string.IsNullOrEmpty(finishContent) == false)
                                this.iconBorder.ToolTip = this.icon.ToolTip = "已废弃 ◆ " + finishContent;
                            else this.iconBorder.ToolTip = this.icon.ToolTip = "已废弃";
                            break;
                        //case "[-]":
                        default:
                            this.iconBorder.BorderBrush = Brushes.Red;
                            if (string.IsNullOrEmpty(finishContent) == false)
                                this.iconBorder.ToolTip = this.icon.ToolTip = "未开始 ◆ " + finishContent;
                            else this.iconBorder.ToolTip = this.icon.ToolTip = "未开始";
                            break;
                    }
                }
            }
        }

        public event EventHandler<DocumentSavedEventArgs> Saved;

        protected void OnSaved()
        {
            UpdateTitleToHeader();

            if (Saved != null)
            {
                Saved(this, new DocumentSavedEventArgs() { FileFullName = this.FullFilePath, });
            }

            SaveAnchorSpans();

            //RefreshFolders();  // 调试CSS时很不方便，还是取消的好。
        }

        /// <summary>
        /// 保存文件后或载入文件后（加密文件）将文件标题刷新到编辑器头部。
        /// </summary>
        public void UpdateTitleToHeader()
        {
            string title = null;
            foreach (var line in this.editorBase.Text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
            {

                if (line.StartsWith("%"))
                {
                    title = line.Substring(1).Trim();
                    break;
                }

                title = CustomMarkdownSupport.GetDocumentTitle(line);
                if (string.IsNullOrWhiteSpace(title) == false) break;

                int startIndex = line.IndexOf("标题＞＞");
                int endIndex = line.IndexOf("＜＜标题");
                if (startIndex >= 0 && endIndex >= 0)
                {
                    title = line.Substring(startIndex + 4, endIndex - startIndex - 4);
                    break;
                }
            }

            if (string.IsNullOrWhiteSpace(title) == false)
            {
                string subTitle;
                if (title.Length > 20)
                {
                    subTitle = title.Substring(0, 20) + "...";
                }
                else subTitle = title;

                this.headerTextBlock.Text = subTitle.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
            }
            else
            {
                this.headerTextBlock.Text = this.ShortFileName.Trim(new char[] { ' ', '\t', '　', '\n', '\r', });  //这个首尾的空行还是不要出现的好。;
            }
        }

        void miPin_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            BetterWpfControls.Panels.ScrollablePanel.SetIsLocked(this, !BetterWpfControls.Panels.ScrollablePanel.GetIsLocked(this));
        }

        /// <summary>
        /// 打开指定路径的 Markdown 文件。
        /// </summary>
        /// <param name="fullPath">要打开的 Markdown 文件的路径。</param>
        /// <returns>顺利完成就返回 string.Empty。</returns>
        internal string OpenDocument(string fullPath)
        {
            if (IsEncrypted(fullPath))
            {
                this.ShortFileName = new FileInfo(fullPath).Name;
                this.FullFilePath = fullPath;

                var fstLine = new StreamReader(fullFilePath).ReadLine();

                var decryptedFstLine = SetPasswordPanel.TextDecrypt(
                    fstLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
                var indexOfPassword = decryptedFstLine.IndexOf("Password:");
                var indexOfQuestion = decryptedFstLine.IndexOf("Question:");
                if (indexOfQuestion >= 0 && indexOfPassword >= 10)
                {
                    this.Password = decryptedFstLine.Substring(indexOfPassword + 9);
                    this.PasswordTip = decryptedFstLine.Substring(9, indexOfPassword - 10);

                    this.InputPasswordPanel.Visibility = Visibility.Visible;
                    if (this.setPasswordPanel.Visibility != Visibility.Collapsed)
                    {
                        this.setPasswordPanel.Visibility = Visibility.Collapsed;
                    }
                    return string.Empty;
                }
            }

            if (string.IsNullOrEmpty(fullPath)) return "未提供有效文件名\r\n";
            if (System.IO.File.Exists(fullPath) == false) return string.Format("　　不存在路径为{0}的文件\r\n", fullPath);

            this.FullFilePath = fullPath;
            this.EditorBase.Load(this.FullFilePath);

            //初始化文件保存状态。
            this.EditorBase.IsModified = false;

            //刷新折叠块状态
            RefreshFolders();

            RefreshEditingStatu();
            return string.Empty;
        }

        /// <summary>
        /// 打开文件后，将一些平时不需要编辑的折叠块自动折叠起来。
        /// （例如：&lt;Script /&gt;、&lt;Math /&gt;、&lt;Style /&gt; 等。）
        /// </summary>
        private void RefreshFolders()
        {
            if (Globals.MainWindow.SupportFolding == false) return;

            if (Globals.MainWindow.PerspectiveMode == Perspective.EditingAndPresentation)
            {
                this.editorBase.FoldingManager.FoldSpecialFoldings = true;
                this.editorBase.FoldingStrategy.UpdateFoldings(
                    this.editorBase.FoldingManager, this.editorBase.Document);
                this.editorBase.FoldingManager.FoldSpecialFoldings = false;
            }
            else
            {
                this.editorBase.FoldingManager.FoldMathFoldings = true;
                this.editorBase.FoldingStrategy.UpdateFoldings(
                    this.editorBase.FoldingManager, this.editorBase.Document);
                this.editorBase.FoldingManager.FoldMathFoldings = false;  // 这个一定要还原，否则折叠区会无法展开。
            }
        }

        /// <summary>
        /// 粘贴图像成链接或粘贴文本。
        /// </summary>
        public void Paste()
        {
            //如果是粘贴图像，粘贴后不再继续操作。
            var result = TryToPasteResourceFileLink();
            if (result == string.Empty) return;
            try
            {
                var clipboardText = Clipboard.GetText();
                if (string.IsNullOrEmpty(clipboardText) == false)
                {
                    this.editorBase.Paste();
                }
                else
                {
                    LMessageBox.Show(result, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning("未能从剪贴板中取得有效数据，请尝试重新复制并粘贴。异常消息是：" + ex.Message);
            }
        }

        /// <summary>
        /// 行分割符，根据操作系统不同而可能不同。这里只是以防万一。
        /// </summary>
        public string Splitter
        {
            get
            {
                string splitter;
                if (this.editorBase.Text.Contains("\r\n"))
                {
                    splitter = "\r\n";
                }
                else if (this.editorBase.Text.Contains("\n"))
                {
                    splitter = "\n";
                }
                else
                {
                    splitter = "\r";
                }
                return splitter;
            }
        }

        /// <summary>
        /// 粘贴源代码。会自动为粘贴的源代码添加行号、行首空格。
        /// </summary>
        public void PasteCode()
        {
            try
            {
                var clipboardText = Clipboard.GetText().Replace("\t", "    ");
                if (string.IsNullOrEmpty(clipboardText)) return;

                string[] lines = clipboardText.TrimEnd(new char[] { '\r', '\n', ' ', '　', '\t', }).Replace("\r", "").Split(new char[] { '\n', }, StringSplitOptions.None);
                if (lines.Length <= 0)
                {
                    this.editorBase.SelectedText = "";
                    return;
                }

                bool allStartsWithFourSpace = true;
                foreach (var line in lines)
                {
                    if (line.StartsWith("    ") == false) allStartsWithFourSpace = false;
                }

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < lines.Length; i++)
                {
                    string line;
                    if (allStartsWithFourSpace)
                    {
                        line = lines[i].Substring(4);
                    }
                    else
                    {
                        line = lines[i];
                    }

                    string s = "    " + (i + 1).ToString("D2") + "." + "  " + line;

                    sb.Append(s + Splitter);
                }

                this.editorBase.SelectedText = Splitter + "    {^}" + Splitter + sb.ToString();
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning("未能从剪贴板中取得有效数据，请尝试重新复制并粘贴。异常消息是：" + ex.Message);
            }
        }

        public void 粘贴成代码()
        {
            PasteCode();
        }

        public Dictionary<string, string> 递归取配置词典(string header, string footer, bool byRegex = false, int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            try
            {
                var fileInfo = new 文件(this.FullFilePath);

                var dict = new Dictionary<string, string>();

                var list = fileInfo.上级文件列表;
                if (list.Count > 0)
                {
                    foreach (var item in list)
                    {
                        var me = Globals.MainWindow.FindMarkdownEditorByPath(item.文件路径);

                        string infoSpan;
                        if (me == null)
                        {
                            infoSpan = 纯文本处理器.取文本片段(File.ReadAllText(item.文件路径), header, footer, byRegex, startIndex, ignoreCase, multiLine);
                        }
                        else
                        {
                            infoSpan = 纯文本处理器.取文本片段(me.EditorBase.Text, header, footer, byRegex, startIndex, ignoreCase, multiLine);
                        }

                        var preDict = 纯文本处理器.取配置词典(infoSpan);
                        if (preDict != null && preDict.Count > 0)
                        {
                            foreach (var key in preDict.Keys)
                            {
                                if (dict.Keys.Contains(key))
                                {
                                    dict[key] = preDict[key];
                                }
                                else
                                {
                                    dict.Add(key, preDict[key]);
                                }
                            }
                        }
                    }
                }

                // 当前文件最后取
                var span = 纯文本处理器.取文本片段(this.EditorBase.Text, header, footer, byRegex, startIndex, ignoreCase, multiLine);

                var dictCur = 纯文本处理器.取配置词典(span);

                if (dict.Count <= 0) return dictCur;

                if (dictCur != null && dictCur.Count > 0)
                {
                    foreach (var key in dictCur.Keys)
                    {
                        if (dict.Keys.Contains(key))
                        {
                            dict[key] = dictCur[key];
                        }
                        else
                        {
                            dict.Add(key, dictCur[key]);
                        }
                    }
                }

                return dict;
            }
            catch
            {
                return null;
            }
        }

        public Dictionary<string, string> 递归取配置字典(string header, string footer, bool byRegex = false, int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return 递归取配置词典(header, footer, byRegex, startIndex, ignoreCase, multiLine);
        }

        public string 取文档标题_实时()
        {
            if (this.editorBase != null) return this.editorBase.取文档标题_实时();

            return "";
        }

        public string 取标题文本_实时()
        {
            return 取文档标题_实时();
        }

        public string 取文档副标题_实时()
        {
            if (this.editorBase != null) return this.editorBase.取文档副标题_实时();

            return "";
        }

        public string 取副标题文本_实时()
        {
            return 取文档副标题_实时();
        }

        public void 开始更改()
        {
            this.editorBase?.BeginChange();
        }

        public void 开始修改()
        {
            this.editorBase?.BeginChange();
        }

        public void 结束更改()
        {
            this.editorBase?.EndChange();
        }

        public void 结束修改()
        {
            this.editorBase?.EndChange();
        }

        public string 取两行之间的文本(int startLineNumber, int endLineNumber)
        {
            return this.editorBase?.取两行之间的文本(startLineNumber, endLineNumber);
        }

        public PythonList 取两行之间的文本行列表(int startLineNumber, int endLineNumber, bool ignoreEmpty = true)
        {
            var text = this.editorBase?.取两行之间的文本(startLineNumber, endLineNumber);
            if (string.IsNullOrEmpty(text)) return null;

            var lines = text.Split(new string[] { "\r\n", "\n", "\r" }, ignoreEmpty ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None);
            var list = new PythonList();
            foreach (var line in lines)
            {
                list.Add(line);
            }
            return list;
        }

        public int 按规则查找文本行号(string rule, int curLineNumber, bool backword = false)
        {
            if (this.editorBase == null) return 0;

            return this.editorBase.按规则查找文本行号(rule, curLineNumber, backword);
        }

        public string 取文本(int offset, int length)
        {
            return this.editorBase?.取文本(offset, length);
        }

        public string 取文本(PythonTuple tuple)
        {
            return this.editorBase?.取文本(tuple);
        }

        public string 取文本片段(string header, string tail, bool byRegex = false,
             int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取文本片段(header, tail, byRegex, startIndex, ignoreCase, multiLine);
        }

        public string 取片段(string header, string tail, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取片段(header, tail, byRegex, startIndex, ignoreCase, multiLine);
        }

        public Tuple<纯文本片段, 纯文本片段, 纯文本片段> 取片段信息(string header, string tail, bool byRegex = false,
             int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取片段信息(header, tail, byRegex, startIndex, ignoreCase, multiLine);
        }

        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取片段信息列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取片段信息列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }

        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取片段信息元组列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取片段信息列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }

        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取逆序片段信息列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取逆序片段信息列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }
        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取逆序片段信息元组列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取逆序片段信息元组列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }

        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取反向片段信息列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取反向片段信息列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }

        public List<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> 取反向片段信息元组列表(
            IronPython.Runtime.PythonList ruleSetList, bool byRegex = false,
            int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取反向片段信息元组列表(ruleSetList, byRegex, startIndex, ignoreCase, multiLine);
        }

        public Tuple<纯文本片段, 纯文本片段, 纯文本片段> 取片段信息元组(string header, string tail, bool byRegex = false,
             int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取片段信息元组(header, tail, byRegex, startIndex, ignoreCase, multiLine);
        }

        public Tuple<纯文本片段, 纯文本片段, 纯文本片段> 取文本片段信息元组(string header, string tail, bool byRegex = false,
             int startIndex = 0, bool ignoreCase = true, bool multiLine = true)
        {
            return this.editorBase?.取文本片段信息元组(header, tail, byRegex, startIndex, ignoreCase, multiLine);
        }

        public bool 按片段信息替换(Tuple<纯文本片段, 纯文本片段, 纯文本片段> spanInfo)
        {
            if (this.editorBase == null) return false;

            return this.editorBase.按片段信息替换(spanInfo);
        }

        public bool 按片段信息列表替换(IList<Tuple<纯文本片段, 纯文本片段, 纯文本片段>> spanInfoList)
        {
            if (this.editorBase == null) return false;

            return this.editorBase.按片段信息列表替换(spanInfoList);
        }

        public void 读取当前附加信息(string header, string tail, bool byRegex = false)
        {
            this.editorBase?.读取当前附加信息(header, tail, byRegex);
        }

        public string 取附加信息(string key, bool readAppendInfoFirst = false)
        {
            return this.editorBase?.取附加信息(key, readAppendInfoFirst);
        }

        public void 替换(int offset, int length, string text)
        {
            this.editorBase?.替换(offset, length, text);
        }

        public DocumentLine 据行号取行对象(int number)
        {
            return this.editorBase?.据行号取行对象(number);
        }

        public DocumentLine 据位置取行对象(int offset)
        {
            return this.editorBase?.据位置取行对象(offset);
        }

        public int 取字符位置(int line, int column)
        {
            if (this.editorBase == null) return -1;

            return this.editorBase.取字符位置(line, column);
        }

        public void 移除字符(int offset, int length)
        {
            this.editorBase?.移除字符(offset, length);
        }

        public string SelectedText
        {
            get => this.editorBase?.SelectedText;
            set
            {
                if (this.editorBase != null)
                {
                    this.editorBase.SelectedText = value;
                }
            }
        }

        public string 选择的文本
        {
            get => this.editorBase?.选择的文本;
            set
            {
                if (this.editorBase != null)
                {
                    this.editorBase.选择的文本 = value;
                }
            }
        }

        public string 活动文本 { get => this.editorBase?.活动文本; }

        public string 选择区左侧文本
        {
            get => this.editorBase?.选择区左侧文本;
        }

        public string 选择区右侧文本
        {
            get => this.editorBase?.选择区右侧文本;
        }

        public string 选择区头到行尾
        {
            get => this.editorBase?.选择区头到行尾;
        }

        public string 选择区尾到行首
        {
            get => this.editorBase?.选择区尾到行首;
        }

        public string 选择区头部所在行文本
        {
            get => this.editorBase?.选择区头部所在行文本;
        }

        public string 选择区尾部所在行文本
        {
            get => this.editorBase?.选择区尾部所在行文本;
        }

        public string 选择区头部前一行文本
        {
            get => this.editorBase?.选择区头部前一行文本;
        }

        public string 选择区尾部后一行文本
        {
            get => this.editorBase?.选择区尾部后一行文本;
        }

        public void 选择文本(int start, int length)
        {
            this.editorBase?.选择文本(start, length);
        }

        public void 置选择区(int start, int length)
        {
            this.editorBase?.置选择区(start, length);
        }

        public void 移动插入点(int append_offset, bool byHead = true)
        {
            this.editorBase?.移动插入点(append_offset, byHead);
        }

        public void 选择区头选到行首()
        {
            this.editorBase?.选择区头选到行首();
        }

        public void 选择区尾选到行尾()
        {
            this.editorBase?.选择区尾选到行尾();
        }

        public void 选择区头选到行尾()
        {
            this.editorBase?.选择区头选到行尾();
        }

        public void 选择区尾选到行头()
        {
            this.editorBase?.选择区尾选到行头();
        }

        public int 选择区头部索引 { get { return editorBase == null ? -1 : editorBase.SelectionStart; } }
        public int 选择区尾部索引 { get { return editorBase == null ? -1 : editorBase.SelectionEnd; } }
        public int 选择区头
        {
            get { return editorBase == null ? -1 : editorBase.SelectionStart; }
        }

        public int 选择区尾
        {
            get { return editorBase == null ? -1 : editorBase.SelectionEnd; }
        }

        public bool 选择前一行文本() => editorBase == null ? false : editorBase.SelectPreviewLine();

        public bool 选择后一行文本() => editorBase == null ? false : editorBase.SelectNextLine();

        public string[] 选择的所有文本行 => editorBase?.selected_lines;

        public string[] 活动的所有文本行 => editorBase?.actived_lines;

        public string 格式化超文本片段(string html)
        {
            return this.editorBase?.格式化超文本片段(html);
        }

        public int 选择区长度 { get { return editorBase == null ? -1 : this.editorBase.SelectionLength; } }

        public static Tuple<string, int> 是否树型文字表行(string line)
        {
            return MarkdownEditorBase.是否树型文字表行(line);
        }

        public 选择区 选择区
        {
            get { return editorBase == null ? null : new 选择区(this.editorBase.SelectionStart, 选择的文本, this.editorBase); }
        }

        #region 供 IronPython 脚本提供的其他方法

        public void 插到行首(int lineNumber, string txt)
        {
            if (txt == null) txt = "";

            if (lineNumber > this.editorBase.Document.LineCount)
            {
                var linesCount = lineNumber - this.editorBase.Document.LineCount;
                this.editorBase.Document.Insert(this.editorBase.Document.TextLength, new string('\n', linesCount).Replace("\n", "\r\n"));
            }

            var line = this.editorBase.Document.GetLineByNumber(lineNumber);
            this.editorBase.Document.Insert(line.Offset, txt);
            this.editorBase.Select(line.Offset + txt.Length, 0);
        }

        public void 插到行尾(int lineNumber, string txt)
        {
            if (txt == null) txt = "";

            if (lineNumber > this.editorBase.Document.LineCount)
            {
                var linesCount = lineNumber - this.editorBase.Document.LineCount;
                this.editorBase.Document.Insert(this.editorBase.Document.TextLength, new string('\n', linesCount).Replace("\n", "\r\n"));

                var line = this.editorBase.Document.GetLineByNumber(lineNumber);
                this.editorBase.Document.Insert(line.EndOffset, txt);
                this.editorBase.Select(line.EndOffset + txt.Length, 0);
            }
            else
            {
                var line = this.editorBase.Document.GetLineByNumber(lineNumber);
                this.editorBase.Document.Insert(line.EndOffset, txt);
                this.editorBase.Select(line.EndOffset, 0);
            }
        }

        public void 置只读属性(bool isReadonly = true)
        {
            this.editorBase?.置只读属性(isReadonly);
        }

        public List<QuestionInfo> 取选择题列表(bool choiceItemsConfounded = true)
        {
            var list = QuestionBuilder.取选择题列表(this.EditorBase.Text, false, choiceItemsConfounded);

            foreach (var q in list)
            {
                q.MarkdownImageLinkRealPath = this.GetRealPathFromRelativeImagePath(q.MarkdownImagePath);
            }

            return list;
        }

        public void 更新选择题列表(List<QuestionInfo> qilst)
        {
            this.editorBase?.更新选择题列表(qilst);
        }

        #endregion

        public List<Question> 取试题列表(bool choiceItemsConfounded = false)
        {
            var content = this.editorBase.Text;
            var errorMsg = new StringBuilder();
            var questions = QuestionBuilder.BuildQuestions(this, errorMsg, true, content, true, choiceItemsConfounded);
            if (questions == null || questions.Count <= 0)
            {
                throw new Exception(errorMsg.ToString());
            }

            // 检验是否有题干相同的试题（选择或判断或填空）！
            var checkResult2 = QuestionBuilder.CheckSameTitleQuestionExists(questions);
            if (string.IsNullOrEmpty(checkResult2) == false)
            {
                throw new Exception(checkResult2);
            }

            var qcount = 0;
            foreach (var q in questions)
            {
                if (q.Type != QuestionType.Text)
                    qcount++;
            }

            if (qcount <= 0)
            {
                throw new Exception("没能在当前演示界面中找到试题。");
            }

            string testpaper_title = "";
            if (string.IsNullOrWhiteSpace(testpaper_title))
            {
                var regTitle = new Regex(@"^%.*$", RegexOptions.Multiline);
                var matchTitle = regTitle.Match(this.EditorBase.Text);
                if (matchTitle.Success)
                {
                    testpaper_title = matchTitle.Value.Trim(new char[] { '%', ' ', '　', '\t', '\r', '\t', }).Replace("\r", "").Replace("\n", "");
                }
            }

            if (string.IsNullOrWhiteSpace(testpaper_title))
                testpaper_title = "新试卷";


            for (int i = 0; i < questions.Count; i++)
            {
                var q = questions[i];
                q.MarkdownImageLinkRealPath = this.GetRealPathFromRelativeImagePath(q.MarkdownImagePath);
            }

            return questions;
        }

        public void ExportQuestionsToPPTX(string saveFilePath = null, string testpaper_title = null)
        {
            if (this.IsModified == true)
            {
                LMessageBox.ShowInfomation("导出试题到 PPTX 文件功能是以磁盘内容而不是当前编辑器的内容为基准的。\r\n\r\n　　请先保存文件。");
                return;
            }

            if (string.IsNullOrWhiteSpace(testpaper_title))
            {
                var regTitle = new Regex(@"^%.*$", RegexOptions.Multiline);
                var matchTitle = regTitle.Match(this.EditorBase.Text);
                if (matchTitle.Success)
                {
                    testpaper_title = matchTitle.Value.Trim(new char[] { '%', ' ', '　', '\t', '\r', '\t', }).Replace("\r", "").Replace("\n", "");
                }
            }

            if (string.IsNullOrWhiteSpace(testpaper_title))
                testpaper_title = "新试卷";
            try
            {
                FileInfo saveFileInfo;
                try
                {
                    saveFileInfo = new FileInfo(saveFilePath);
                }
                catch
                {
                    SaveFileDialog sfd = new SaveFileDialog() { Title = Globals.AppName, };
                    sfd.Filter = "PPTX 文档(*.pptx)|*.pptx";
                    sfd.FilterIndex = 0;
                    sfd.FileName = testpaper_title;

                    var defaultOutpupDirectoryPath = Globals.PathOfWorkspace + "$_OutputDocs~";
                    sfd.InitialDirectory = defaultOutpupDirectoryPath;   // Globals.PathOfDeskTop;

                    if (Directory.Exists(defaultOutpupDirectoryPath) == false)
                    {
                        Directory.CreateDirectory(defaultOutpupDirectoryPath);
                    }

                    if (sfd.ShowDialog() != true) return;

                    saveFilePath = sfd.FileName;
                    saveFileInfo = new FileInfo(saveFilePath);
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
                return;
            }

            try
            {
                var questions = 取试题列表(true);
                if (questions == null || questions.Count == 0) return;

                var pptxDoc = new Utils.NPOIHelp.演示文稿管理器(幻灯片尺寸.宽屏幻灯片);   // 这里不能直接传入路径，否则会被视为打开现有文件。

                var fstSlide = pptxDoc.首幻灯片;
                if (fstSlide == null)
                {
                    fstSlide = pptxDoc.添加幻灯片();
                }

                var report = fstSlide.创建报表(3, 1);

                var tbxTitle = fstSlide.添加文本块(0, 1, 1, 1, testpaper_title, true);
                tbxTitle.置形状字体名("宋体");
                tbxTitle.置形状字体字号(42);
                var trimChars = new char[] { ' ', '　', '\t', '\r', '\n' };

                var numberOfQuestion = 1;

                foreach (var q in questions)
                {
                    switch (q.Type)
                    {
                        case QuestionType.Choice:
                        case QuestionType.Judge:    // 判断题是只有两个选项的选择题
                            {
                                // 每题一张幻灯片
                                var qSlide = pptxDoc.添加幻灯片();
                                var qReport = qSlide.创建报表(5 + Math.Max(4, q.ChoiceItems.Count), 16);
                                // 为使判断、选择的题干纵向尽量相等，至少留足四个选项的纵向空间

                                形状 title;

                                var baseTitle = q.TitleString.Replace("\r", "");
                                var indexOfLineBreak = baseTitle.IndexOf("\n");

                                if (indexOfLineBreak < 0 || (q.TitleString.Contains("｜") == false && q.TitleString.Contains("|") == false))
                                {
                                    var pieces = Utils.Text.SplitToPiecesByRegex(@"(?<=【).{1,}?(?=】)", q.TitleString.Trim(trimChars));
                                    var titleBuilder = new StringBuilder();
                                    foreach (var piece in pieces)
                                    {
                                        if (piece.IsMartchText)
                                        {
                                            titleBuilder.Append("  ");
                                        }
                                        else
                                        {
                                            titleBuilder.Append(piece.SourceText.Replace("\\_", "_"));
                                        }
                                    }

                                    title = qSlide.添加文本块(0, 0, 16, 3, numberOfQuestion.ToString() + ". " + titleBuilder.ToString().Trim(trimChars), true).置形状字体字号(24);
                                    title.置形状边框宽(0).置形状文本对齐("l").置形状边框色("FFFFFF");
                                }
                                else
                                {
                                    var fstLine = baseTitle.Substring(0, indexOfLineBreak);

                                    var pieces = Utils.Text.SplitToPiecesByRegex(@"(?<=【).{1,}?(?=】)", fstLine.Trim(trimChars));
                                    var titleBuilder = new StringBuilder();
                                    foreach (var piece in pieces)
                                    {
                                        if (piece.IsMartchText)
                                        {
                                            titleBuilder.Append("  ");
                                        }
                                        else
                                        {
                                            titleBuilder.Append(piece.SourceText.Replace("\\_", "_"));
                                        }
                                    }

                                    title = qSlide.添加文本块(0, 0, 16, 3, numberOfQuestion.ToString() + ". " + titleBuilder.ToString().Trim(trimChars), true).置形状字体字号(24);
                                    title.置形状边框宽(0).置形状文本对齐("l").置形状边框色("FFFFFF");

                                    var lines = baseTitle.Substring(indexOfLineBreak).Split(new char[] { '\n', }, StringSplitOptions.None);

                                    qSlide.插入二维文字表(lines.Join("\r\n"), 10, 4, 6, 4, true);

                                }

                                var imagePath = this.GetRealPathFromRelativeImagePath(q.MarkdownImagePath);
                                if (System.IO.File.Exists(imagePath))
                                {
                                    qSlide.添加图像形状(10, 4, 6, Math.Max(4, q.ChoiceItems.Count), imagePath, true);
                                }

                                for (int i = 0; i < q.ChoiceItems.Count; i++)
                                {
                                    qSlide.添加文本块(0, 4 + i, 10, 1, QuestionBuilder.ConvertToAlphaNumber(i + 1) + ". " + q.ChoiceItems[i].Text.Trim(trimChars), true)
                                          .置形状字体字号(24)
                                          .置形状边框宽(0)
                                          .置形状文本对齐("l")
                                          .置形状边框色("FFFFFF");
                                }

                                numberOfQuestion++;
                                break;
                            }
                        case QuestionType.FillBlank:
                            {
                                //TODO: 此功能尚未完成（2025.2.6，还没想好如何布局，有选择题暂时也可以用了）
                                break;
                            }
                        case QuestionType.Subjective:
                            {
                                //TODO: 此功能尚未完成（2025.2.6，还没想好如何布局，有选择题暂时也可以用了）
                                break;
                            }
                    }
                }

                var pptxdocInfo = pptxDoc.另存演示文稿(saveFilePath);
                var answer = LMessageBox.Show($"文件已导出至：\r\n\r\n　　{saveFilePath}。\r\n\r\n　　要尝试打开吗？",
                        Globals.AppName, System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Question);
                if (answer == System.Windows.MessageBoxResult.Yes)
                {
                    pptxdocInfo.打开自身();
                }
            }
            catch (Exception ex)
            {
                LMessageBox.ShowWarning(ex.Message);
            }
        }
    }

    /// <summary>
    /// 此类用于文件被保存时传递文件路径。
    /// </summary>
    public class DocumentSavedEventArgs : EventArgs
    {
        public string FileFullName { get; set; }
    }
}
